randomly select position in array with condition - python

I have an array like this:
A = [[1,0,2,3],
[2,0,1,1],
[3,1,0,0]]
and I want to get the position of one of the cells with the value == 1 such as A[0][0] or A[1][2] and so on ...
So far I did this:
A = np.array([[1,0,2,3],
[2,0,1,1],
[3,1,0,0]])
B = np.where(A == 1)
C = []
for i in range(len(B[0])):
Ca = [B[0][i], B[1][i]]
C.append(Ca)
D = random.choice(C)
But now I want to reuse D for getting a cell value back. Like:
A[D] (which does not work) should return the same as A[1][2]
Does someone how to fix this or knows even a better solution?

This should work for you.
A = np.array([[1,0,2,3],
[2,0,1,1],
[3,1,0,0]])
B = np.where(A == 1)
C = []
for i in range(len(B[0])):
Ca = [B[0][i], B[1][i]]
C.append(Ca)
D = random.choice(C)
print(A[D[0]][D[1]])
This gives the output.
>>> print(A[D[0]][D[1]])
1
Since the value of D would be of the sort [X,Y], the value could be obtained from the matrix as A[D[0]][D[1]]

It seems you are trying to randomly select one of the cells where A is 1. You can use numpy for this all the way through instead of having to resort to for-loops
B = np.array(np.where(A == 1))
>>> B
array([[0, 1, 1, 2],
[0, 2, 3, 1]])
Now to randomly select a column corresponding to one of the cells, we can use np.random.randint
column = np.random.randint(B.shape[1])
D = B[:, column]
>>> D
array([1, 2]) # corresponds to the second index pair in B
Now you can simply index into A using the tuple of D (to correpond to the dimensions' indices) as
>>> A[tuple(D)]
1

Related

"To location array" to index array

I have some data array
a = [6,4,3,1,5]
and a "to location array", that gives indices for how these elements should be reshuffled,
b = [4,2,0,1,3]
What this means: the 1st element of a should go to index 4, the 2nd element of a should go to index 2, and so on. So when applying b to a, I should get:
c = [3,1,4,5,6]
However, I am struggling to implement this operation. Is it a common operation? If so, could someone give me an example? I want to generate some array d from b so that
c = a[d]
In the above example,
d = [2,3,1,4,0]
What I tried: d = b[b], but this is not always correct.
If you don't need d, you can directly get c with np.put_along_axis:
>>> a = np.array([6,4,3,1,5])
>>> b = np.array([4,2,0,1,3])
>>> c = np.zeros_like(b)
>>> np.put_along_axis(c, b, a, 0)
>>> c
array([3, 1, 4, 5, 6])
Note the operation is inplace.
One method I just found is calculating d is as follows:
d = np.zeros(5);
d[b] = np.arange(5)
But it seems very non-intuitive to me.

How to split integers within a two-dimensional list in Python 3?

Say I have:
data = [[1, 2], [3, 4]]
And I want to define a function that will take this one list argument, 'data', and be able to separate the integers within the first and second instances.
After, it needs to take each of those integers that shared each instance and subtract it from one another like this:
1 - 2 (which would be -1)
3 - 4 (which would be -1)
And then take those numbers and finds the product and returns the final integer (-1 * -1) = 1
Kind of lost as to how I would go about this.
You can do this easily with unpacking:
data = [[1, 2], [3, 4]]
d1, d2 = data
a, b, c, d = *d1, *d2
res = (a - b) * (c - d)
print(res)
# 1
If there are arbitrary number of sublists, you would better iterate:
lst = []
for d in data:
a, b = d
lst.append(a - b)
res = 1
for x in lst:
res *= x
print(res)
# 1

Create sub groups with the position of element from a list

I have to create sub groups with the position of element from a list based on the value of elements which means sub groups will contain the same elements from the list.
However, the size of the sub group will depend on the value of the group.
Example: A = [2,1,1,2,1] will create [0, 3] [1] [2] [4]
I have written the below code. However, all the test cases are not getting passed.
I am not getting correct output if A = [2,1,2,1,2,2]. For my case I am getting:
0 2
4 5
1
3
However, expectation is:
0 2
1
3
4 5
Below is my code:
A = [2,1,1,2,1]
uniqueCounts = []
for x in A:
if x not in uniqueCounts:
uniqueCounts.append(x)
for i in range(len(uniqueCounts)):
n = uniqueCounts[i]
p = [i for i,x in enumerate(A) if x == n]
if(len(p)==1):
print(*p)
else:
q = int(len(p)/n)
count = 0
c = 0
while count < q:
print(*p[c:n+c])
c = c + n
count = count + 1
You should use numpy and the function np.where():
import numpy as np
A = [2,1,1,2,1]
A = np.asarray(A)
ID = np.where(A == 2)[0]
Output:
ID = array([0, 3], dtype=int64)
You can then work with this array to transform it into list, count the number of element using the shape or len... and so on.
To get back to your code:
A = [2,1,1,2,1]
uniqueCounts = list(set(A))
set is returning a set without duplicates. converting it to list gives you the uniqueCounts directly.
Conclusion:
A = [2,1,1,2,1]
uniqueCounts = list(set(A))
A = np.asarray(A)
subgroup = list()
for elt in uniqueCounts:
subgroup.append(tuple(np.where(A == elt)[0]))
Some work is still needed to get the desired output, I'll let that to you :)
First sort the list A with only unique elements. without losing the order. Then find all the indices for the elements in the sorted list from the original list
A = [2,1,1,2,1]
sortedA = sorted(set(A), key=A.index)
# [2, 1]
[[i for i,x in enumerate(A) if x==e] for e in sortedA]
# [[0, 3], [1, 2, 4]]

Compare two lists to return a list with all elements as 0 except the ones which matched while keeping index?

I am a bit stuck on this:
a = [1,2,3,2,4,5]
b = [2,5]
I want to compare the two lists and generate a list with the same items as a, but with any items that don't occur in b set to 0. Valid outputs would be these:
c = [0,2,0,0,0,5]
# or
c = [0,0,0,2,0,5]
I would not know the number elements in either list beforehand.
I tried for loops but
['0' for x in a if x not in b]
It removes all instances of 2. Which I only want to remove once(it occurs once in b for the moment). I need to add a condition in the above loop to keep elements which match.
The following would work:
a = [1,2,3,2,4,5]
b = [2, 5]
output = []
for x in a:
if x in b:
b.remove(x)
output.append(x)
else:
output.append(0)
or for a one-liner, using the fact that b.remove(x) returns None:
a = [1,2,3,2,4,5]
b = {2, 5}
output = [(b.remove(x) or x) if x in b else 0 for x in a]
If the elements in b are unique, this is best done with a set, because sets allow very efficient membership testing:
a = [1,2,3,2,4,5]
b = {2, 5} # make this a set
result = []
for num in a:
# If this number occurs in b, remove it from b.
# Otherwise, append a 0.
if num in b:
b.remove(num)
result.append(num)
else:
result.append(0)
# result: [0, 2, 0, 0, 0, 5]
If b can contain duplicates, you can replace the set with a Counter, which represents a multiset:
import collections
a = [1,2,3,2,4,5]
b = collections.Counter([2, 2, 5])
result = []
for num in a:
if b[num] > 0:
b[num] -= 1
result.append(num)
else:
result.append(0)
# result: [0, 2, 0, 2, 0, 5]
Here's one way using set. Downside is the list copy operation and initial set conversion. Upside is O(1) removal and lookup operations.
a = [1,2,3,2,4,5]
b = [2,5]
b_set = set(b)
c = a.copy()
for i in range(len(c)):
if c[i] in b_set:
b_set.remove(c[i])
else:
c[i] = 0
print(c)
[0, 2, 0, 0, 0, 5]

Deleting values from multiple arrays that have a particular value

Lets say I have two arrays: a = array([1,2,3,0,4,5,0]) and b = array([1,2,3,4,0,5,6]). I am interested in removing the instances where a and bare 0. But I also want to remove the corresponding instances from both lists. Therefore what I want to end up with is a = array([1,2,3,5]) and b = array([1,2,3,5]). This is because a[3] == 0 and a[6] == 0, so both b[3] and b[6] are also deleted. Likewise, since b[4] == 0, a[4] is also deleted.Its simple to do this for say two arrays:
import numpy as np
a = np.array([1,2,3,0,4,5,0])
b = np.array([1,2,3,4,0,5,6])
ix = np.where(b == 0)
b = np.delete(b, ix)
a = np.delete(a, ix)
ix = np.where(a == 0)
b = np.delete(b, ix)
a = np.delete(a, ix)
However this solution doesnt scale up if I have many many arrays (which I do). What would be a more elegant way to do this?
If I try the following:
import numpy as np
a = np.array([1,2,3,0,4,5,0])
b = np.array([1,2,3,4,0,5,6])
arrays = [a,b]
for array in arrays:
ix = np.where(array == 0)
b = np.delete(b, ix)
a = np.delete(a, ix)
I get a = array([1, 2, 3, 4]) and b = array([1, 2, 3, 0]), not the answers I need. Any idea where this is wrong?
Assuming both/all arrays always have the same length, you can use masks:
ma = a != 0 # mask elements which are not equal to zero in a
mb = b != 0 # mask elements which are not equal to zero in b
m = ma * mb # assign the intersection of ma and mb to m
print a[m], b[m] # [1 2 3 5] [1 2 3 5]
You can of course also do it in one line
m = (a != 0) * (b != 0)
Or use the inverse
ma = a == 0
mb = b == 0
m = ~(ma + mb) # not the union of ma and mb
This is happening because when you return from np.delete, you get an array that is stored in b and a inside the loop. However, the arrays stored in the arrays variable are copies, not references. Hence, when you're updating the arrays by deleting them, it deletes with regard to the original arrays. The first loop will return the corrects indices of 0 in the array but the second loop will return ix as 4 (look at the original array).Like if you display the arrays variable in each iteration, it is going to remain the same.
You need to reassign arrays once you are done processing one array so that it's taken into consideration the next iteration. Here's how you'd do it -
a = np.array([1, 2, 3, 0, 4, 5, 0])
b = np.array([1, 2, 3, 4, 0, 5, 6])
arrays = [a,b]
for i in range(0, len(arrays)):
ix = np.where(arrays[i] == 0)
b = np.delete(b, ix)
a = np.delete(a, ix)
arrays = [a, b]
Of course you can automate what happens inside the loop. I just wanted to give an explanation of what was happening.
A slow method involves operating over the whole list twice, first to build an intermediate list of indices to delete, and then second to delete all of the values at those indices:
import numpy as np
a = np.array([1,2,3,0,4,5,0])
b = np.array([1,2,3,4,0,5,6])
arrays = [a, b]
vals = []
for array in arrays:
ix = np.where(array == 0)
vals.extend([y for x in ix for y in x.tolist()])
vals = list(set(vals))
new_array = []
for array in arrays:
new_array.append(np.delete(array, vals))
Building up on top of Christoph Terasa's answer, you can use array operations instead of for loops:
arrays = np.vstack([a,b]) # ...long list of arrays of equal length
zeroind = (arrays==0).max(0)
pos_arrays = arrays[:,~zeroind] # a 2d array only containing those columns where none of the lines contained zeros

Categories