Element (array) "multiplication" vs. list comprehension initialization [duplicate] - python

This question already has an answer here:
Having trouble making a list of lists of a designated size [duplicate]
(1 answer)
Closed 9 years ago.
In relation to the question:
How to initialize a two-dimensional array in Python?
While working with a two dimentional array, I found that initializing it in a certain way would produce unexpected results. I would like to understand the difference between initializing an 8x8 grid in these two ways:
>>> a = [[1]*8]*8
>>> a
[[1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1], \
[1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1], \
[1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1], \
[1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1]]
vs.
>>> A = [[1 for i in range(8)] for j in range(8)]
>>> A
[[1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1], \
[1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1], \
[1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1], \
[1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1]]
The unexpected results were that any element accessed with the indeces [0-6][x] would point to the last row in [7][x]. The arrays look identical in the interpreter, hence my confusion. What is wrong with the first approach?
If relevant, these arrays hold references to GTK EventBoxes that represent the squares of a chess board. After changing the initialization approach to the list-comprehension method, the squares respond properly to the intended hover and click events.

When you create the 2D array using a = [[1]*8]*8 then the * operator creates 8 references to the same object. Therefore, [1]*8 means create an array of size 8 where all 8 elements are the same object (same reference). Since all elements are the same references updating the value to which that reference points will change the value of every element in the array.
Using the list comprehension A = [[1 for i in range(8)] for j in range(8)] ensures that each element in your 2D array is uniquely referenced. This avoids the erroneous behavior you were seeing where all of the elements were updating simultaneously.

In your first version, you're creating a list containing the number 1 and by multiplying it 8 times create a list containing 8 1s, and using that list 8 times to create a.
So when you change anything in the first version, you'll see that change everywhere else. Your problem being that you're reusing the same instance, something that doesn't happen in the second version.

maybe refactoring the first way make it eeasier to understand:
one_item = [1]
row = one_item*8
matrix = row*8
as you can see, the array of arrays has eight references to row, which means
(a[0] is a[1]) and (a[1] is a[2]) ...
try this for example:
a = [[1]*8]*8
a[0][0] = 2
b = [[1 for i in range(8)] for j in range(8)]
b[0][0] = 3
print a
print b
print (a[0] is a[1]) and (a[1] is a[2]) and (a[2] is a[3]) # true
print (b[0] is b[1]) and (b[1] is b[2]) and (b[2] is b[3]) # false

Related

Python - Replacing Values Leading Up To 1s in an Array

Pretend I have a pandas Series that consists of 0s and 1s, but this can work with numpy arrays or any iterable. I would like to create a formula that would take an array and an input n and then return a new series that contains 1s at the nth indices leading up to every time that there is at least a single 1 in the original series. Here is an example:
array = np.array([0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1])
> preceding_indices_function(array, 2)
np.array([0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1])
For each time there is a 1 in the input array, the two indices preceding it are filled in with 1 regardless of whether there is a 0 or 1 in that index in the original array.
I would really appreciate some help on this. Thanks!
Use a convolution with np.convolve:
N = 2
# craft a custom kernel
kernel = np.ones(2*N+1)
kernel[-N:] = 0
# array([1, 1, 1, 0, 0])
out = (np.convolve(array, kernel, mode='same') != 0).astype(int)
Output:
array([0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1])
Unless you don't want to use numpy, mozway's transpose is the best solution.
But since several iterations have been given, I add my itertools based solution
[a or b or c for a,b,c in itertools.zip_longest(array, array[1:], array[2:], fillvalue=0)]
zip_longest is the same as classical zip, but if the iterators have different "lengths", the number of iteration is the one of the longest, and finished iterators will return None. Unless you add a fillvalue parameter to zip_longest.
So, here itertools.zip_longest(array, array[1:], array[2:], fillvalue=0) gives a sequence of triplets (a,b,c), of 3 subsequent elements (a being the current element, b the next, c the one after, b and c being 0 if there isn't any next element or element after the next).
So from there, a simple comprehension build a list of [a or b or c] that is 1 if a, or b or c is 1, 0 else.
import numpy as np
array = np.array([0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1])
array = np.array([a or array[idx+1] or array[idx+2] for idx, a in enumerate(array[:-2])] + [array[-2] or array[-1]] + [array[-1]])
this function works if a is a list, should work with other iterables as well:
def preceding_indices_function(array, n):
for i in range(len(a)):
if array[i] == 1:
for j in range(n):
if i-j-1 >= 0:
array[i-j-1] = 1
return array
I got a solution that is similar to the other one but slightly simpler in my opinion:
>>> [1 if (array[i+1] == 1 or array[i+2] == 1) else x for i,x in enumerate(array) if i < len(array) - 2]
[0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1]

Special shuffling of the array

I want to shuffle my numpy array a = [2, 2, 2, 1, 1] in this way: a = [2, 1, 2, 1, 2]. So that the same elements do not stand side by side if possible. I know about numpy.array.shuffle but it generates all possible permutations uniformly. Therefore, with the same probability, can appear a = [2, 1, 2, 1, 2] or a = [2, 2, 2, 1, 1]. Is there vectorised solution for more difficult arrays? For example, for this b = np.hstack([np.ones(101), np.ones(50) * 2, np.ones(20) * 3]) array.

pseudo-randomize a list without repeats; while loop is not efficient

I am creating an conditioning experiment with three conditions (0,1,2) and need to pseudo-randomize condition order. I need a randomize list with each condition occurring only 2 times in a row. Here how I tried to achieve it. The code is running but it takes an eternity...
Any ideas why this code is not working well and any different approaches to solve the problem?
#create a list with 36 values
types = [0] * 4 + [1] * 18 + [2]*14 #0=CS+ ohne Verstärkung; 1 = CS-, 2=CS+ mit shock
#random.shuffle(types)
while '1,1,1' or '2,2,2' or '0,0,0' in types:
random.shuffle(types)
else: print(types)
Thank you in advance!
Martina
Your loop has several problems. First while '1,1,1' or '2,2,2' or '0,0,0' in types: is the same as while ('1,1,1') or ('2,2,2') or ('0,0,0' in types):. Non-zero strings are always True so your condition is always true and the while never stops. Even if it did, types is a list of integers. '0,0,0' is a string and is not an element of the list.
itertools.groupby is a good tool to solve this problem. Its an iterator that is designed to group a sequence into subiterators. You can use it to see if any clusters of numbers are too long.
import random
import itertools
#create a list with 36 values
types = [0] * 4 + [1] * 18 + [2]*14 #
print(types)
while True:
random.shuffle(types)
# try to find spans that are too long
for key, subiter in itertools.groupby(types):
if len(list(subiter)) >= 3:
break # found one, staty in while
else:
break # found none, leave while
print(types)
while '1,1,1' or '2,2,2' or '0,0,0' in types:
random.shuffle(types)
evaluates as:
while True or True or '0,0,0' in types:
random.shuffle(types)
and short-circuits at while True
Instead, use: any() which returns True if any of the inner terms are True
Additionally, your types is numbers and you're comparing it to strings:
>>> types
[0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2]
so you need to map those numbers to a string which can be compared:
>>> ','.join(map(str, types))
'0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2'
Try:
while any(run in ','.join(map(str, types)) for run in ['0,0,0', '1,1,1', '2,2,2']):
random.shuffle(types)
>>> types
[1, 2, 1, 2, 1, 2, 1, 1, 0, 2, 0, 1, 2, 1, 1, 2, 1, 2, 1, 2, 2, 1, 1, 2, 0, 2, 1, 1, 0, 2, 1, 1, 2, 2, 1, 1]

Numpy finding element index in another array

I have an array/set with unique positive integers, i.e.
>>> unique = np.unique(np.random.choice(100, 4, replace=False))
And an array containing multiple elements sampled from this previous array, such as
>>> A = np.random.choice(unique, 100)
I want to map the values of the array A to the position of which those values occur in unique.
So far the best solution I found is through a mapping array:
>>> table = np.zeros(unique.max()+1, unique.dtype)
>>> table[unique] = np.arange(unique.size)
The above assigns to each element the index on the array, and thus, can be used later to map A through advanced indexing:
>>> table[A]
array([2, 2, 3, 3, 3, 3, 1, 1, 1, 0, 2, 0, 1, 0, 2, 1, 0, 0, 2, 3, 0, 0, 0,
0, 3, 3, 2, 1, 0, 0, 0, 2, 1, 0, 3, 0, 1, 3, 0, 1, 2, 3, 3, 3, 3, 1,
3, 0, 1, 2, 0, 0, 2, 3, 1, 0, 3, 2, 3, 3, 3, 1, 1, 2, 0, 0, 2, 0, 2,
3, 1, 1, 3, 3, 2, 1, 2, 0, 2, 1, 0, 1, 2, 0, 2, 0, 1, 3, 0, 2, 0, 1,
3, 2, 2, 1, 3, 0, 3, 3], dtype=int32)
Which already gives me the proper solution. However, if the unique numbers in unique are very sparse and large, this approach implies creating a very large table array just to store a few numbers for later mapping.
Is there any better solution?
NOTE: both A and unique are sample arrays, not real arrays. So the question is not how to generate positional indexes, it is just how to efficiently map elements of A to indexes in unique, the pseudocode of what I'd like to speedup in numpy is as follows,
B = np.zeros_like(A)
for i in range(A.size):
B[i] = unique.index(A[i])
(assuming unique is a list in the above pseudocode).
The table approach described in your question is the best option when unique if pretty dense, but unique.searchsorted(A) should produce the same result and doesn't require unique to be dense. searchsorted is great with ints, if anyone is trying to do this kind of thing with floats which have precision limitations, consider something like this.
You can use standard python dict with np.vectorize
inds = {e:i for i, e in enumerate(unique)}
B = np.vectorize(inds.get)(A)
The numpy_indexed package (disclaimer: I am its author) contains a vectorized equivalent of list.index, which does not require memory proportional to the max element, but only proportional to the input itself:
import numpy_indexed as npi
npi.indices(unique, A)
Note that it also works for arbitrary dtypes and dimensions. Also, the array being queried does not need to be unique; the first index encountered will be returned, the same as for list.

Python Two-Dimensional Query..

I am seeing a very unusual behavior in python.. Kindly let me know what am i doing wrong!!
bc = [[0]*(n+1)]*(n+1)
for i in range(n+1):
bc[i][i] = 1
print (bc)
Output
[[1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]]
I am trying to initialize the diagonal elements of two dimensional array to 1, but it is initializing all the elements with 1. I think I am doing something wrong with accessing two dimensional Array..
Also, kindly let me know how can I use two loops to access all the elements of two dimensional array.. my next step..
Thanks.
Your array is initialized incorrectly. The correct way to initialize a 2d array is this:
bc = [[0 for i in xrange(n + 1)] for i in xrange(n + 1)]
It's a common mistake, but the * operator copies the pointer to an list rather than copying the list, so while it looks like you have a 2d list, you actually have a 1d list of pointers to the same list.
the problem is that each array in your array is the same array in memory. you need a new array each time. the [[0]]*6 will for example make 6 of the same arrays in an array, editing one of them will update the other ones.
e.g.
>>> x=[1]
>>> y=x
>>> x.append(3)
>>> x
[1, 3]
>>> y
[1, 3]
>>> z=[x]*3
>>> x.append(6)
>>> z
[[1, 3, 4, 6], [1, 3, 4, 6], [1, 3, 4, 6]]
here is a fix by simply editing bc to be n+1 different arrays:
n=4
bc = [[0]*(n+1) for i in range(n+1)]
for i in range(n+1):
bc[i][i] = 1
print (bc)
[[1, 0, 0, 0, 0], [0, 1, 0, 0, 0], [0, 0, 1, 0, 0], [0, 0, 0, 1, 0], [0, 0, 0, 0, 1]]
Try this one:
bc = [[0 for i in range(n+1)] for j in range(n+1)]
In your example you have only one (!) instance of [0] which is referenced multiple times. So if you change that instance, all references are changed.

Categories

Resources