0

I am new to Python, and I'm struggling to program a function that fills a matrix.I was attempting to write a sensing function for a robot, yet I noticed something I wasn't quite getting. I fill a list of a list iterating it in a loop and checking a condition so as to know which value to assign. When I print the current element, the program seems to be giving me what I want, but when I print the entire list of list, it is filled with zeros. How is this possible? Thanks in advance

p=[[0.05,0.05,0.05,0.05, 0.05],[0.05,0.05,0.05,0.05, 0.05],[0.05,0.05,0.05,0.05, 0.05],[0.05,0.05,0.05,0.05, 0.05]]
measurement='G'
sensor_right=0.7
colors = [['R','G','G','R','R'],['R','R','G','R','R'],['R','R','G','G','R'],['R','R','R','R','R']]
sensetable= [ [0.0] * len(p[0]) ]*len(p)
for i in range(len(p)):  
  for j in range(len(p[0])):
    if (colors[i][j]==measurement):
      sensetable[i][j]=1

    else:
      sensetable[i][j]=0


print(sensetable)

UPDATE: Thank you so much for all of your help. This was all part of a function, which takes into account the probabilities stored in p. I attach the code below. This was the best way I thought it could be done by reading your replies. I do not see a way of using nested list comprehension in this case, where I do need p[i][j] in my calculations inside the loop. Please correct me if I am wrong or if you have further suggestions. Thank you so much!!

def sense(p,measurement,sensor_right,colors):
sensetable=[]

for i in range(len(p)):
    aux=[]
    for j in range(len(p[0])):
        aux.append(p[i][j]*sensor_right if colors[i][j]==measurement else p[i][j]*(1-sensor_right))

    sensetable.append(aux)

norm=sum(sum(sensetable,[]))
probTableFin = [[float(j)/norm for j in i] for i in sensetable]

return probTableFin
  • what is the use of p? Looking at the code, both 'p' & 'colors' are of same size. Hence you can loop over colors as well. Though, using list comprehension is the best. Also using List Comprehension, you don't even need to define sensetable. Do correct if my interpretation is wrong – Mehul Gupta Apr 01 '20 at 16:01
  • Hello @MehulGupta! I'm about to add an update – Elisa Hidalgo Apr 01 '20 at 21:51

3 Answers3

0
sensetable = [[int(colors[i][j] == measurement) for j in range(len(p[i]))] for i in range(len(p))]

Breakdown:

colors[i][j] == measurement returns a boolean, int() converts it to 0/1

Then it is just basic list comprehension. Refer to this for a tutorial :)

And the code

p=[[0.05,0.05,0.05,0.05, 0.05],[0.05,0.05,0.05,0.05, 0.05],[0.05,0.05,0.05,0.05, 0.05],[0.05,0.05,0.05,0.05, 0.05]]
measurement='G'
sensor_right=0.7
colors = [['R','G','G','R','R'],['R','R','G','R','R'],['R','R','G','G','R'],['R','R','R','R','R']]
sensetable = [[int(colors[i][j] == measurement) for j in range(len(p[i]))] for i in range(len(p))]

print (sensetable)

# gives [[0, 1, 1, 0, 0], [0, 0, 1, 0, 0], [0, 0, 1, 1, 0], [0, 0, 0, 0, 0]]

Explanation to why your code doesn't work

The problematic line is sensetable = [[0.0] * len(p[0])] * len(p)

Let me simplify it to sensetable = [[0] * 3] * 5. Works the same, has the same problem, easier to type and explain :)

So basically in python, a = [0] * 3 creates a list [0, 0, 0]. No problem there.

However, problem arises when you call b = [a] * 5. This will create a list [a, a, a, a, a]. However, each of the a in the list is the same thing. What this means is they're the same thing, and they share the same memory address i.e. they're stored at the same place in your memory. Since they're sharing the same memory address, when you change one of them, you'll change all of them. Meaning, if you say b[0][0] = 1, it'll change a[0] to 1, and the list will become [[1,0,0],[1,0,0],..] instead of the expected [[1,0,0],[0,0,0],...].

You can also see this by using the id function.

a = [0] * 3
print (id(a[0]), id(a[1]), id(a[2]))

b = [a] * 5
print (id(b[0]), id(b[1]), id(b[2])) # All `id` are the same!

To fix this, replace b = [a] * 5 with b = [a for _ in range(5)], or equivalently, b = [[0] * 3 for _ in range(5)]. This creates a new copy of a for every b[0], b[1], b[2], b[3], b[4]. This makes them all disjoint, and they're not related to each other.

c = [[0] * 3 for _ in range(5)]
print (id(c[0]), id(c[1]), id(c[2])) # Different id

To learn more: List of lists changes reflected across sublists unexpectedly

Gareth Ma
  • 707
  • 4
  • 10
0

Problem

The notation sensetable = [[0] * len(p[0])] * len(p) makes in fact a list with one sublist copied X times, so as the last sublist to read contains only zeros you get zeros all at the end.

print(sensetable) # [[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]]
sensetable[0][0] = 15
print(sensetable) # [[15, 0, 0, 0, 0], [15, 0, 0, 0, 0], [15, 0, 0, 0, 0], [15, 0, 0, 0, 0]]

Solution

As you iterate over all the values you don't need to pre-fill a list, just build it when iterating

for i in range(len(p)):
    tmp = []
    for j in range(len(p[0])):
        if colors[i][j] == measurement:
            tmp.append(1)
        else:
            tmp.append(0)
    sensetable.append(tmp)

Improvement

  1. Inline the if

    for i in range(len(p)):
        tmp = []
        for j in range(len(p[0])):
            tmp.append(1 if colors[i][j] == measurement else 0)
        sensetable.append(tmp)
    
  2. Improve the append, as int(True)==1, int(False)==0 you can do

    tmp.append(colors[i][j] == measurement)
    
  3. Use list comprehension once

    for i in range(len(p)):
        sensetable.append([colors[i][j] == measurement for j in range(len(p[0]))])
    
  4. Use list comprehension twice and no more indice, but directly the items

    sensetable = [[int(letter == measurement) for letter in sublist] for sublist in colors]
    
azro
  • 53,056
  • 7
  • 34
  • 70
  • This is what I was missing! I checked what @Shubham Sharma said about initializing a matrix and it solves the problem. Is this the correct Python approach though? I couldn't find a way to do it without initializing the matrix. – Elisa Hidalgo Apr 01 '20 at 15:53
  • @ElisaHidalgo You don't need to initialize it, look my part *Solution* you just need to append the values – azro Apr 01 '20 at 15:54
0
sensetable = [[1 if letter==measurement else 0 for letter in color] for color in colors]

You don't need to separately define sensetable !! Code explanation: The above nested list comprehension goes through each element of 'colors' & checks the given condition. 'color': each list in 'colors', 'letter': each element of each list from colors. 'measurement':'G'

Mehul Gupta
  • 1,829
  • 3
  • 17
  • 33