Extract the index value from array - python

I have an array A1 with shape 2x3 & list A2. I want to extract the index value of array from the list.
Example
A1 = [[0, 1, 2]
[3, 4, 5]] # Shape 2 rows & 3 columns
A2 = [0,1,2,3,4,5]
Now, I want to write a code to access the an element's index in Array A1
Expected Output
A2[3] = (1,0) #(1 = row & 0 = column) Index of No.3 in A1
Please help me. Thank you

There is some ambiguity in the question. Are we looking for the indices of elements by value, or by order?
Unravel an ordinal index
Assuming that the values in A1 are not important (i.e. this is not a search of certain values, but really finding the index corresponding to a location), you can use unravel_index for that.
Example:
>>> np.unravel_index(3, A1.shape)
(1, 0)
Or, on the whole A2 in one shot:
>>> np.unravel_index(A2, np.array(A1).shape)
(array([0, 0, 0, 1, 1, 1]), array([0, 1, 2, 0, 1, 2]))
which you may prefer as a list of tuples ("transpose" of the above):
>>> list(zip(*np.unravel_index(A2, np.array(A1).shape)))
[(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2)]
Search for a value
If, instead, you are searching for values, e.g., where in A1 are there values equal to A2[i], then, like in #dc_Bita98's answer:
>>> tuple(np.argwhere(A1 == A2[3]).squeeze())
(1, 0)
If you want all the locations in one shot, you need to do something to handle the fact that the shapes are different. Say also, for sake of illustration, that:
A3 = np.array([9, 1, 0, 1])
Then, either:
>>> i, j, k = np.where(A1 == A3[:, None, None])
>>> out = np.full(A3.shape, (,), dtype=object)
>>> out[i] = list(zip(j, k))
>>> out.tolist()
[None, (1, 0), (2, 0), (3, 0)]
which clearly indicates that the first value (9) was not found, and where to find the others.
Or:
>>> [tuple(np.argwhere(A1 == v).squeeze()) for v in A3]
[None, (0, 1), (0, 0), (0, 1)]

If you can use numpy, check out argwhere
a1 = np.array([[0,1,2],[3,4,5]])
a2 = [0,1,2,3,4,5]
a3 = np.argwhere(a1 == a2[3]).squeeze() # -> (1, 0)

Related

remove some elements from a matrix according to the indices

I have a matrix:
a = ([[1, 0, 0, 0],
[0, 0, 1, 1],
[0, 1, 0, 1]
[1, 0, 0, 1]])
and I want to print the 0s in the matrix but not all of the 0s. I only want to keep the 0s in every row with the smallest index and remove all subsequent zeros in the row.
For instance, in the first row of this matrix, the second element (a[0][1]) should be kept and the rest of elements in the first row should be deleted since they are all zeros.
I used pop() for 2D array but I got attribute error. And the output is not correct too. I don't know how to compare indices and select the smallest column index in every row.
This is my code:
for ix, row in enumerate(a):
for iy, i in enumerate(row):
if i==0 and (iy+ix<(iy+1)+ix) :
a[ix].pop((iy+1))
print(ix,iy)
elif i==0 and (iy+ix>(iy+1)+ix):
a[ix].pop(iy)
print(ix,iy+1)
print(a)
my expected result is the set of indices and the modified matrix a.
0 1
1 0
2 0
3 1
a=[[1,0],[0,1,1],[0,1,1],[1,0]]
Could anyone help me?
This solution only works if there is at least one zero in every row.
indices = []
for x,row in enumerate(a):
i = row.index(0)
indices.append((x,i))
a[x] = row[:i+1] + [e for e in row[i:] if e]
print(indices)
print(a)
Output
[(0, 1), (1, 0), (2, 0), (3, 1)]
[[1, 0], [0, 1, 1], [0, 1, 1], [1, 0, 1]]
Assuming there's a zero in every row, you can get its column index with
c = np.argmin(a, axis=1)
Alternatively, if the matrix can contain negative numbers, you can do
c = np.argmax(np.equal(a, 0), axis=1)
The rows are just
r = np.arange(len(a))
The result you want is then
result = np.stack((r, c), axis=-1)
If there are rows without a zero in them, you can filter the result with a mask:
mask = np.array(a)[r, c] == 0
result = result[mask, :]
Looking at your example input
a = [[1,0,0,0],[0,0,1,1],[0,1,0,1],[1,0,0,1]]
and the expected output
>>[(0, 1), (1, 0), (2, 0), (3, 1)]
you can reframe the problem as finding the index of the element in each row which has the value zero (and where more than one element exists, return the first).
By framing it this way, the solution is as simple as iterating through each row of a and retrieving the index of the value 0 (whereby only the first element will be returned by default).
Using list comprehension that would look like this:
value_to_find = 0
desired_indexes = [
row.index(value_to_find) for row in a
]
or using map that would be:
value_to_find = 0
desired_indexes = map(lambda row:row.index(value_to_find), a)
Then you could enumerate them to pair the results with the row number
enumerate(desired_indexes)
Et voila!
>>[(0, 1), (1, 0), (2, 0), (3, 1)]
The entire solution can be written in a single line like so:
answer = list(enumerate(map(lambda row:row.index(0), a)))
try this:
a = [[1, 0, 0, 0],
[0, 0, 1, 1],
[0, 1, 0, 1],
[1, 0, 0, 1]]
b = []
for i in a:
f = False
c = []
for j in i:
if (j==0 and f==False) or j != 0:
c.append(j)
if j == 0: f = True
else:
continue
b.append(c)
output:
[[1, 0], [0, 1, 1], [0, 1, 1], [1, 0, 1]]
For getting indices zero in array you can try this:
list({i : j.index(0) for i,j in enumerate(b)}.items())
# [(0, 1), (1, 0), (2, 0), (3, 1)]

Set Pandas DataFrame entries by boolean index to tuple values

I have a data frame
a b
0 1 (1, 1, 0)
1 1 (1, 1, 0)
2 2 (1, 1, 0)
3 1 (1, 1, 0)
(created by d = pd.DataFrame({'a':[1,1,2,1], 'b':[(1,1,0)]*4})).
I'd like to assign tuple values to entries indexed by boolean values, e.g.
d.loc[d['a']==1, 'b'] = [(0,0,1)] * 3
to change the values in rows 0,1,3 to (0,0,1). This does not work and throws a ValueError: Must have equal len keys and value when setting with an ndarray. Note that d.loc[d['a']==1, 'b'] = [((0,0,1),)]*3 does not throw an error, but the result is
a b
0 1 ((0, 0, 1),)
1 1 ((0, 0, 1),)
2 2 (1, 1, 0)
3 1 ((0, 0, 1),)
How do I get the result
a b
0 1 (0, 0, 1)
1 1 (0, 0, 1)
2 2 (1, 1, 0)
3 1 (0, 0, 1)
using logical indexing for rows?
Here's a way you can do:
# set values
ixs = [0,1,3]
vals = [[(0,0,1)]*len(ixs)]
# replace values
d.loc[ixs,['b']] = vals
a b
0 1 (0, 0, 1)
1 1 (0, 0, 1)
2 2 (1, 1, 0)
3 1 (0, 0, 1)
For pandas >= 1.0, you can do:
d.loc[ixs, 'b'] = pd.Series(vals, index=ixs)
Just double wrap the tuple inside list
d.loc[d['a']==1, 'b'] = [[(0, 0, 1)]]
Out[78]:
a b
0 1 (0, 0, 1)
1 1 (0, 0, 1)
2 2 (1, 1, 0)
3 1 (0, 0, 1)
One way to do is to create a Series. However, the indices have to match:
d.loc[d['a']==1, 'b'] = pd.Series([(0,0,1)]*len(d.loc[d['a']==1, 'b']), index=d.loc[d['a']==1, 'b'].index)
This seems a bit cumbersome and I hope someone else posts a better solution.
(Using the naive d.loc[d['a']==1, 'b'] = pd.Series([(0,0,1)]*len(d.loc[d['a']==1, 'b'])) produces NaN in the last row, because the index 3 of the data frame is not met by a matching index in the series. This: d.loc[d['a']==1, 'b'] = pd.Series([(0,0,1)]*len(d)) also seems to work, but seems terribly inefficient especially when most conditions are false.)
here is a solution, you can change the values to fit with your answer

More elegant way of find a range of repeating elements

I have this problem.
let l be a list containing only 0's and 1's, find all tuples that represents the start and end of a repeating sequence of 1's.
example
l=[1,1,0,0,0,1,1,1,0,1]
answer:
[(0,2),(5,8),(9,10)]
i solved the problem with the following code, but i think it is pretty messy, i would like to know if there is a cleaner way to solve this problem (maybe using map/reduce ?)
from collections import deque
def find_range(l):
pairs=deque((i,i+1) for i,e in enumerate(l) if e==1)
ans=[]
p=[0,0]
while(len(pairs)>1):
act=pairs.popleft()
nex=pairs[0]
if p==[0,0]:
p=list(act)
if act[1]==nex[0]:
p[1]=nex[1]
else:
ans.append(tuple(p))
p=[0,0]
if(len(pairs)==1):
if p==[0,0]:
ans.append(pairs.pop())
else:
ans.append((p[0],pairs.pop()[1]))
return ans
With itertools.groupby magic:
from itertools import groupby
lst = [1, 1, 0, 0, 0, 1, 1, 1, 0, 1]
indices, res = range(len(lst)), []
for k, group in groupby(indices, key=lambda i: lst[i]):
if k == 1:
group = list(group)
sl = group[0], group[-1] + 1
res.append(sl)
print(res)
The output:
[(0, 2), (5, 8), (9, 10)]
Or with a more efficient generator function:
def get_ones_coords(lst):
indices = range(len(lst))
for k, group in groupby(indices, key=lambda i: lst[i]):
if k == 1:
group = list(group)
yield group[0], group[-1] + 1
lst = [1, 1, 0, 0, 0, 1, 1, 1, 0, 1]
print(list(get_ones_coords(lst))) # [(0, 2), (5, 8), (9, 10)]
As a short bonus, here's alternative numpy approach, though sophisticated, based on discrete difference between consecutive numbers (numpy.diff) and extracting indices of non-zero items (numpy.faltnonzero):
In [137]: lst = [1,1,0,0,0,1,1,1,0,1]
In [138]: arr = np.array(lst)
In [139]: np.flatnonzero(np.diff(np.r_[0, arr, 0]) != 0).reshape(-1, 2)
Out[139]:
array([[ 0, 2],
[ 5, 8],
[ 9, 10]])
Code:
a = [[l.index(1)]]
[l[i] and len(a[-1])==2 and a.append([i]) or l[i] or len(a[-1])==1 and a[-1].append(i) for i in range(len(l))]
Output:
[[0, 2], [5, 8], [9]]
Code:
l=[1,1,0,0,0,1,1,1,0,1]
indices = [ind for ind, elem in enumerate(l) if elem == 1]
diff = [0]+[x - indices[i - 1] for i, x in enumerate(indices)][1:]
change_ind = [0]+[i for i, change in enumerate(diff) if change > 1]+[len(indices)]
split_indices = [tuple(indices[i:j]) for i,j in zip(change_ind,change_ind[1:])]
proper_tuples = [(tup[0], tup[-1]) if len(tup)>2 else tup for tup in split_indices]
print(proper_tuples)
Logic:
indices is the list of indices where l elements = 1 => [0, 1, 5, 6, 7, 9]
diff calculates the difference between the indices found above and appends a 0 at the start to keep their lengths the same => [0, 1, 4, 1, 1, 2]
change_ind indicates the locations where a split needs to happen which corresponds to where diff is greater than 1. Also append the first index and last index for later use or else you will only have the middle tuple => [0, 2, 5, 6]
split_indices creates tuples based on the range indicated in consecutive elements in change_ind (using zip which creates the combination of ranges) => [(0, 1), (5, 6, 7), (9,)]
Lastly, proper_tuples loops through the tuples create in split_indices and insures that if their length is greater than 2, then only consider the first and last elements, otherwise keep as is => [(0, 1), (5, 7), (9,)]
Output:
[(0, 1), (5, 7), (9,)]
Final Comments:
Although this does not match what OP suggested in the original question:
[(0,2),(5,8),(9,10)]
It does make more logical sense and seems to follow what OP indicated in the comments.
For example, at the start of l there are two ones - so the tuple should be (0, 1) not (0, 2) to match the proposed (start, end) notation.
Likewise at the end there is only a single one - so the tuple corresponding to this is (9,) not (9, 10)

How to change an element in a list of lists if it has a specific index and condition that is met?

I want to be able to take a list of lists (lst) and a list of indexes and those elements in lst that have that have those indexes and also meet the condition ( == '1') to be changed to '0'.
If I input
lst = [['1','2','3'],[],['4','2','1']]
and
specific_indexes = [(0, 0), (0, 2), (2, 0), (2, 2)]
I get [['0', '2', '3'], [], ['4', '2', '0']]
but I would like faster way to do this.
def change(lst, specific_indexes):
for (x,y) in specific_indexes:
if lst[y][x] == '1':
lst[y][x] = '0'
return lst
...but I would like faster way to do this.
If you are interested in performance, you can use a specialist 3rd party library such as NumPy. This does mean you have to define a regular 2d array as an input, or transform it into one as shown below.
import numpy as np
lst = [['1','2','3'],[],['4','2','1']]
idx = [(0, 0), (0, 2), (2, 0), (2, 2)]
# calculate column number and construct NumPy array
colnum = max(map(len, lst))
arr = np.array([sublst if sublst else ['0'] * colnum for sublst in lst]).astype(int)
idx = np.array(idx)
# calculate indexer and mask array conditionally
mask = np.ix_(idx[:, 1], idx[:, 0])
arr[mask] = np.where(arr[mask] == 1, 0, arr[mask])
print(arr)
# array([[0, 2, 3],
# [0, 0, 0],
# [4, 2, 0]])

How do I find a value relative to where a list occurs within my list in Python?

I have a list of numbers:
Data = [0,2,0,1,2,1,0,2,0,2,0,1,2,0,2,1,1,...]
And I have a list of tuples of two, which is all possible combinations of the individual numbers above:
Combinations = [(0,0),(0,1),(0,2),(1,0),(1,1),(1,2),(2,0),(2,1),(2,2)]
I want to try to find where each item in Combinations appears in Data and add the value after each occurrence to another list.
For example, for (0,2) I want to make a list [0,0,0,1] because those are the the values that fall immediately after (0,2) occurs in Data.
So far, I have:
any(Data[i:i+len(CurrentTuple)] == CurrentTuple for i in xrange(len(Data)-len(CurrentTuple)+1))
Where CurrentTuple is Combinations.pop().
The problem is that this only gives me a Boolean of whether the CurrentTuple occurs in Data. What I really need is the value after each occurrence in Data.
Does anyone have any ideas as to how this can be solved? Thanks!
You can use a dict to group the data to see where/if any comb lands in the original list zipping up pairs:
it1, it2 = iter(Data), iter(Data)
next(it2)
Combinations = [(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2), (2, 0), (2, 1), (2, 2)]
d = {c: [] for c in Combinations}
ind = 2
for i, j in zip(it1, it2):
if (i, j) in d and ind < len(Data):
d[(i, j)].append(Data[ind])
ind += 1
print(d)
Which would give you:
{(0, 1): [2, 2], (1, 2): [1, 0], (0, 0): [], (2, 1): [0, 1], (1, 1): [2], (2, 0): [1, 2, 1, 2], (2, 2): [], (1, 0): [2], (0, 2): [0, 0, 0, 1]}
You could also do it in reverse:
from collections import defaultdict
it1, it2 = iter(Data), iter(Data)
next(it2)
next_ele_dict = defaultdict(list)
data_iter = iter(Data[2:])
for ind, (i, j) in enumerate(zip(it1, it2)):
if ind < len(Data) -2:
next_ele_dict[(i, j)].append(next(data_iter))
def next_ele():
for comb in set(Combinations):
if comb in next_ele_dict:
yield comb, next_ele_dict[comb]
print(list(next_ele()))
Which would give you:
[((0, 1), [2, 2]), ((1, 2), [1, 0]), ((2, 1), [0, 1]), ((1, 1), [2]), ((2, 0), [1, 2, 1, 2]), ((1, 0), [2]), ((0, 2), [0, 0, 0, 1])]
Any approach is better than a pass over the Data list for every element in Combinations.
To work for arbitrary length tuples we just need to create the tuples based on the length:
from collections import defaultdict
n = 2
next_ele_dict = defaultdict(list)
def chunks(iterable, n):
for i in range(len(iterable)-n):
yield tuple(iterable[i:i+n])
data_iter = iter(Data[n:])
for tup in chunks(Data, n):
next_ele_dict[tup].append(next(data_iter))
def next_ele():
for comb in set(Combinations):
if comb in next_ele_dict:
yield comb, next_ele_dict[comb]
print(list(next_ele()))
You can apply it to whatever implementation you prefer, the logic will be the same as far as making the tuples goes.
sum([all(x) for x in (Data[i:i+len(CurrentTuple)] == CurrentTuple for i in xrange
(len(Data)-len(CurrentTuple)+1))])
What you did return a generator that produce the following list:
[array([False, True], dtype=bool),
array([ True, False], dtype=bool),
array([False, True], dtype=bool),
...
array([False, False], dtype=bool),
array([False, False], dtype=bool),
array([False, False], dtype=bool),
array([False, True], dtype=bool)]
One of the array that you have in this list match the CurrentTuple only if both the bool in the array are True. The all return True only if all the elements of the list are True so the list generated by the [all(x) for x in ...] will contains True only if the twin of numbers match the CurrentTuple. A True is conted as 1 when you use sum. I hope it is clear.
If you want to compare only non-overlapping pairs:
[2,2,
0,2,
...]
and keep the algorithm as general as possible, you can use the following:
sum([all(x) for x in (Data[i:i+len(CurrentTuple)] == CurrentTuple for i in xrange
(0,len(Data)-len(CurrentTuple)+1,len(CurrentTuple)))])
Despite much more cryptic, this code is much faster than any alternative that using append (look [Comparing list comprehensions and explicit loops (3 array generators faster than 1 for loop) to understand why).

Categories