Home > database >  Loop/list problem: Seemingly only the last indices are considered and then copied to every else entr
Loop/list problem: Seemingly only the last indices are considered and then copied to every else entr

Time:07-20

I try to make a table where I calculate an expectation value and vary 3 different parameters i,j,k (and also average over 20 values each). The expected result is a (4,6,5) table/list structure that has 120 different entries depending on the parameters indexed by i,j,k. The final step where I average the values does not work. The actual result is that there are only 5 different entries that appear 24 times each. I recreated a similar simplified problem where the averaging is not necessary as the values for a certain (i,j,k) do not differ, but where zt is the input list with different entries (i times j times k), and z is the output list that should show all possible combinations of (i times j times k), but it does not. It appears that only the last (i,j) is taken for each k. The problem is obviously in the loop, but I have no clue why it does not work as it should. Below is the code and the result z.

import numpy as np
zt = np.zeros([4,6,5,20])
z =  [[['']*5]*6]*4
for i in range(4):
    for j in range(6):
        for k in range(5):
            for l in range(20):
                zt[i][j][k][l] = i*j*k
            E0mean = np.sum(zt[i][j][k])/20 #Mean of the n calculations
            E0dev = max(zt[i][j][k])-min(zt[i][j][k])/2 #uncertaincy
            z[i][j][k] = str(round(E0mean,5)) u"\u00B1" str(round(E0dev,5)) #results: mean  - uncertainty
z


Output:
[[['0.0±0.0', '15.0±0.0', '30.0±0.0', '45.0±0.0', '60.0±0.0'],
  ['0.0±0.0', '15.0±0.0', '30.0±0.0', '45.0±0.0', '60.0±0.0'],
  ['0.0±0.0', '15.0±0.0', '30.0±0.0', '45.0±0.0', '60.0±0.0'],
  ['0.0±0.0', '15.0±0.0', '30.0±0.0', '45.0±0.0', '60.0±0.0'],
  ['0.0±0.0', '15.0±0.0', '30.0±0.0', '45.0±0.0', '60.0±0.0'],
  ['0.0±0.0', '15.0±0.0', '30.0±0.0', '45.0±0.0', '60.0±0.0']],
 [['0.0±0.0', '15.0±0.0', '30.0±0.0', '45.0±0.0', '60.0±0.0'],
  ['0.0±0.0', '15.0±0.0', '30.0±0.0', '45.0±0.0', '60.0±0.0'],
  ['0.0±0.0', '15.0±0.0', '30.0±0.0', '45.0±0.0', '60.0±0.0'],
  ['0.0±0.0', '15.0±0.0', '30.0±0.0', '45.0±0.0', '60.0±0.0'],
  ['0.0±0.0', '15.0±0.0', '30.0±0.0', '45.0±0.0', '60.0±0.0'],
  ['0.0±0.0', '15.0±0.0', '30.0±0.0', '45.0±0.0', '60.0±0.0']],
 [['0.0±0.0', '15.0±0.0', '30.0±0.0', '45.0±0.0', '60.0±0.0'],
  ['0.0±0.0', '15.0±0.0', '30.0±0.0', '45.0±0.0', '60.0±0.0'],
  ['0.0±0.0', '15.0±0.0', '30.0±0.0', '45.0±0.0', '60.0±0.0'],
  ['0.0±0.0', '15.0±0.0', '30.0±0.0', '45.0±0.0', '60.0±0.0'],
  ['0.0±0.0', '15.0±0.0', '30.0±0.0', '45.0±0.0', '60.0±0.0'],
  ['0.0±0.0', '15.0±0.0', '30.0±0.0', '45.0±0.0', '60.0±0.0']],
 [['0.0±0.0', '15.0±0.0', '30.0±0.0', '45.0±0.0', '60.0±0.0'],
  ['0.0±0.0', '15.0±0.0', '30.0±0.0', '45.0±0.0', '60.0±0.0'],
  ['0.0±0.0', '15.0±0.0', '30.0±0.0', '45.0±0.0', '60.0±0.0'],
  ['0.0±0.0', '15.0±0.0', '30.0±0.0', '45.0±0.0', '60.0±0.0'],
  ['0.0±0.0', '15.0±0.0', '30.0±0.0', '45.0±0.0', '60.0±0.0'],
  ['0.0±0.0', '15.0±0.0', '30.0±0.0', '45.0±0.0', '60.0±0.0']]]

CodePudding user response:

When using [xxx]*3, I make a list of 3 items, all referencing the same object. That list multiplication does not make copies. This is a python 101 gotcha.

In [178]: alist =[['xxx']]*3    
In [179]: alist
Out[179]: [['xxx'], ['xxx'], ['xxx']]    
In [180]: alist[0]
Out[180]: ['xxx']    

Changing one reference changes them all:

In [181]: alist[0][0]='yyy'    
In [182]: alist
Out[182]: [['yyy'], ['yyy'], ['yyy']]

Sometimes the * replication works, but you have to be careful. It's better to use a list comprehension like this:

In [183]: alist = [['xxx'] for _ in range(3)]
In [184]: alist
Out[184]: [['xxx'], ['xxx'], ['xxx']]
In [185]: alist[0][0]='yyy'
In [186]: alist
Out[186]: [['yyy'], ['xxx'], ['xxx']]

Each ['xxx'] is a new list, not a duplicate reference.

A numpy requivalent is:

In [188]: arr = np.full((3,1),'xxx')    
In [189]: arr
Out[189]: 
array([['xxx'],
       ['xxx'],
       ['xxx']], dtype='<U3')    
In [191]: arr[0,0]='yyy'    
In [192]: arr
Out[192]: 
array([['yyy'],
       ['xxx'],
       ['xxx']], dtype='<U3')

Often when building a list of lists it's easier to use append:

In [193]: alist = []
     ...: for i in range(3):
     ...:     blist = []
     ...:     for j in range(4):
     ...:         blist.append(f'<{i},{j}>')
     ...:     alist.append(blist)
     ...:     

In [194]: alist
Out[194]: 
[['<0,0>', '<0,1>', '<0,2>', '<0,3>'],
 ['<1,0>', '<1,1>', '<1,2>', '<1,3>'],
 ['<2,0>', '<2,1>', '<2,2>', '<2,3>']]

The list comprehension equilvalent:

In [196]: [[f'<{i},{j}>' for j in range(4)] for i in range(3)]
Out[196]: 
[['<0,0>', '<0,1>', '<0,2>', '<0,3>'],
 ['<1,0>', '<1,1>', '<1,2>', '<1,3>'],
 ['<2,0>', '<2,1>', '<2,2>', '<2,3>']]

CodePudding user response:

I think this does what you want:

import numpy as np
zt = np.zeros([4,6,5,20])
z =  []
for i in range(4):
    z2 = []
    for j in range(6):
        z3 = []
        for k in range(5):
            for l in range(20):
                zt[i][j][k][l] = i*j*k
            E0mean = np.sum(zt[i][j][k])/20 #Mean of the n calculations
            E0dev = max(zt[i][j][k])-min(zt[i][j][k])/2 #uncertaincy
            z3.append( str(round(E0mean,5)) u"\u00B1" str(round(E0dev,5)) ) #results: mean  - uncertainty
        z2.append(z3)
    z.append(z2)
  • Related