Initializing matrix python3 - python-3.x

I don't know whether this is a bug, or I got a wrong semantic meaning of the * token in arrays:
>>> arr = [None] * 5 # Initialize array of 5 'None' items
>>> arr
[None, None, None, None, None]
>>> arr[2] = "banana"
>>> arr
[None, None, 'banana', None, None]
>>> # right?
...
>>> mx = [ [None] * 3 ] * 2 # initialize a 3x2 matrix with 'None' items
>>> mx
[[None, None, None], [None, None, None]]
>>> # so far, so good, but then:
...
>>> mx[0][0] = "banana"
>>> mx
[['banana', None, None], ['banana', None, None]]
>>> # Huh?
Is this a bug, or did I got the wrong semantic meaning of the __mult__ token?

You're copying the same reference to the list multiple times. Do it like this:
matrix = [[None]*3 for i in range(2)]

Related

Efficiently shorten a list till first and last appearance of a value different from None

I have lists of the style:
[None, ..., None, "c", 1, 3, None, None, 4.3, "b", None, "4", None, ..., None]
that I would like to efficiently shorten to:
["c", 1, 3, None, None, 4.3, "b", None, "4"]
Straight forward I could do:
def _shorten(long_list):
start = False
short_list = []
for e in long_list:
if e:
start = True
if start:
short_list.append(e)
return short_list
reversed(_shorten(reversed(_shorten(long_list))))
Try:
lst = [None, None, None, None]
i = 0
while i < len(lst) - 1 and lst[i] is None:
i += 1
j = len(lst) - 1
while j > 0 and lst[j] is None:
j -= 1
print(lst[i : j + 1])
EDIT: Removed the first O(n^2) answer

Issue in getting the comments from YAML using ruamel.yaml

Code:
import ruamel.yaml
yaml_str = """\
# comments start
a: 52
# comment for a
b: 50
# comment for b
c: 50
# comment for c
d:
# comment for d
e: 60
# comment for e
f: 70
# comment for f
"""
yaml = ruamel.yaml.YAML()
data = yaml.load(yaml_str)
print(data.ca.comment)
print(data.ca.items)
Output:
[None, [CommentToken('# comments start\n', line: 0, col: 0)]]
{'a': [None, None, CommentToken('\n# comment for a\n', line: 2, col: 0), None], 'b': [None, None, CommentToken('\n# comment for b\n', line: 4, col: 0), None], 'c': [None, None, CommentToken('\n# comment for c\n', line: 6, col: 0), None], 'd': [None, None, None, [CommentToken('# comment for d\n', line: 8, col: 4)]]}
Question:
Why isn't it showing comments pertaining to the keys e and f?
What is the correct way to retrieve the comments based on the key say for example. How to get the comments for the key e ( # comment for e) ?
In ruamel.yaml most comments are attached to the dict (or list) like
structure containing the key (or element) after which the comment
occurred.
To get to the comments following keys e and f you need to look at the dict
that is the value for d:
print(data['d'].ca.items)
print('comment post commment for "e":', repr(data['d'].ca.get('e', 2).value))
which gives:
{'e': [None, None, CommentToken('\n # comment for e\n', line: 10, col: 3), None], 'f': [None, None, CommentToken('\n # comment for f\n', line: 12, col: 3), None]}
comment post commment for "e": '\n # comment for e\n'
Please note that the comment for e starts with a newline, indicating there is no end-of-line comment

print mismatch items in two array

I want to compare two array(4 floating point)and print mismatched items.
I used this code:
>>> from numpy.testing import assert_allclose as np_assert_allclose
>>> x=np.array([1,2,3])
>>> y=np.array([1,0,3])
>>> np_assert_allclose(x,y, rtol=1e-4)
AssertionError:
Not equal to tolerance rtol=0.0001, atol=0
(mismatch 33.33333333333333%)
x: array([1, 2, 3])
y: array([1, 0, 3])
the problem by this code is with big array:
(mismatch 0.0015104228617559556%)
x: array([ 0.440088, 0.35994 , 0.308225, ..., 0.199546, 0.226758, 0.2312 ])
y: array([ 0.44009, 0.35994, 0.30822, ..., 0.19955, 0.22676, 0.2312 ])
I can not find what values are mismatched. how can see them ?
Just use
~np.isclose(x, y, rtol=1e-4) # array([False, True, False], dtype=bool)
e.g.
d = ~np.isclose(x, y, rtol=1e-4)
print(x[d]) # [2]
print(y[d]) # [0]
or, to get the indices
np.where(d) # (array([1]),)

SUM numbers in a nested list

Currently I am trying to sum numbers from two nested loops. I am very close but for some reason, the numbers are not adding properly.
def addTables(table1,table2):
counter = 0
index = 0
table3 = [[None] * len(table1[0])] * len(table1)
print(table3)
for row in table1:
for i in table1[0]:
table3[counter][index] = table1[counter][index] + table2[counter][index]
index = index +1
index = 0
counter = counter + 1
print (table3)
My values are table1 = [[1,2,3],[4,5,6]] and table2 = [[1,2,3],[4,5,6]].
For some reason, it is printing
[[None, None, None], [None, None, None]]
[[2, 4, 6], [2, 4, 6]]
[[8, 10, 12], [8, 10, 12]]
but i want it to print
[[None, None, None], [None, None, None]]
[[2, 4, 6], [None, None, None]]
[[2, 4, 6], [8, 10, 12]]
I think this line is wrong, but I have no idea how to fix it.
table3[counter][index] = table1[counter][index] + table2[counter]
The problem is in
table3 = [[None] * len(table1[0])] * len(table1)
Multiplying a list actually copies references to its items; it doesn't duplicate the items in the list. To see what happens, look at your code like this:
subtable = [None] * len(table1[0])]
# subtable = [None, None, None]
table3 = [subtable] * len(table1)
# table3 = [subtable, subtable]
Thus, table3[0] and table3[1] are actually the same list. So when you set table3[0][0], you're actually modifying subtable, which is table3[0] and table3[1]!
You can see this effect for yourself:
>>> table3 = [[None] * 3] * 2
>>> table3
[[None, None, None], [None, None, None]]
>>> table3[0][1] = 5
>>> table3
[[None, 5, None], [None, 5, None]]
You can fix this by using list comprehensions to construct table3:
>>> table3 = [[None for x in table1[0]] for y in table1]
>>> table3
[[None, None, None], [None, None, None]]
>>> table3[0][1] = 5
>>> table3
[[None, 5, None], [None, None, None]]
Alternatively, using list multiplication for the inner list is fine. (This is because the references to None get replaced, while references to a sublist get modified in place):
>>> table3 = [[None] * len(table1[0]) for y in table1]
>>> table3
[[None, None, None], [None, None, None]]
>>> table3[0][1] = 5
>>> table3
[[None, 5, None], [None, None, None]]
This subtlety may be confusing though. Using a nested list comprehension is more explicit.
But personally, I wouldn't construct the list ahead of time like that in the first place. Instead, I would recommend starting with an empty list, and appending to that as you go
table3 = []
for row in table1:
sumrow = []
index = 0
for i in table1[0]:
sumrow.append(table1[counter][index] + table2[counter][index])
index += 1
table3.append(sumrow)
counter += 1
And, building upon that, it's usually cleaner to iterate over lists directly, rather than iterating over their indices. You can iterate over two equally-sized lists by using zip, like so:
for row1, row2 in zip(table1, table2):
sumrow = []
for item1, item2 in zip(row1, row2):
sumrow.append(item1 + item2)
table3.append(sumrow)
And once we're there, we could express sumrow as a list comprehension:
for row1, row2 in zip(table1, table2):
table3.append([item1 + item2 for item1, item2 in zip(row1, row2)])
Note that this pairwise addition in the list comprehension can also be achieved by applying sum to every pair using map:
for row1, row2 in zip(table1, table2):
table3.append(list(map(sum, zip(row1, row2))))
And then we could also replace the outer for loop by a list comprehension.
table3 = [list(map(sum, zip(row1, row2))) for row1, row2 in zip(table1, table2)]
Which can be slightly improved by using list unpacking for the rows:
table3 = [list(map(sum, zip(*rows))) for rows in zip(table1, table2)]
I'm a little conflicted if this is actually the best / most readable approach, so maybe I should've stopped a few versions ago. But hey, here we are ;)

how to operate None in the list comprehension?

I want to make [2,3,None,4,None] into [4,6,None,8,None],how can i do?
>>> v= [2,3,None,4,None]
>>> [x*2 for x in v if not x is None]
[4, 6, 8]
You're almost there. Just add an else clause to include the Nones:
>>> [x*2 if x is not None else x for x in v]
[4, 6, None, 8, None]
Note that you must put the if-else before the for x in v; otherwise you'd get a syntax error.
Personally, I'd rewrite it a little further to make the behavior more explicit, but it's all up to you.
>>> [None if x is None else x*2 for x in v]
[4, 6, None, 8, None]
If you are able to use NaN instead of None, you can simplify your code, since NaN already behaves in the desired way, i.e. 2*nan evaluates to nan again, so you don't need to special-case it:
>>> nan = float('nan')
>>> v = [2, 3, nan, 4, nan]
>>> [2 * x for x in v]
[4, 6, nan, 8, nan]

Resources