Use bool list to retrieve elements from another list - Python - python

I have this situation:
main_list = [12, 10, 30, 10, 11,10, 31]
get_indices = [1, 0, 1, 1, 0, 0, 0]
What I want to do is, extract elements from main_list according to the boolean value in get_indices.
I tried the following but it is not working :
result = main_list[bool(get_indices)]
print result
10
It should be 12, 30, 10

I just came across compress() which is best suited for this task.
compress() generates an iterator for efficient looping.
from itertools import compress
main_list = [12, 10, 30, 10, 11,10, 31]
get_indices = [1, 0, 1, 1, 0, 0, 0]
print list(compress(main_list, get_indices))
[12, 30, 10]

You can use list comprehension -
result = [x for i, x in enumerate(main_list) if get_indices[i]]
You do not need to use bool() , 0 is considered False-like in boolean context, and anything non-zero is True-like.
Example/Demo -
>>> main_list = [12, 10, 30, 10, 11,10, 31]
>>> get_indices = [1, 0, 1, 1, 0, 0, 0]
>>> result = [x for i, x in enumerate(main_list) if get_indices[i]]
>>> result
[12, 30, 10]
Or if you really want something similar to your method, since you say -
But if I was able to get my method working, I could also do for example : result = main_list[~bool(get_indices)] to get : [10, 11, 10, 31]
You can use numpy , convert the lists to numpy arrays. Example -
In [30]: import numpy as np
In [31]: main_list = [12, 10, 30, 10, 11,10, 31]
In [32]: get_indices = [1, 0, 1, 1, 0, 0, 0]
In [33]: main_array = np.array(main_list)
In [34]: get_ind_array = np.array(get_indices).astype(bool)
In [35]: main_array[get_ind_array]
Out[35]: array([12, 30, 10])
In [36]: main_array[~get_ind_array]
Out[36]: array([10, 11, 10, 31])

Related

Moving specific number (without sort) to the left of list

I want to just move the zero's to left and don't want to sort the list.
For example, if my list is like:
nums = [1, 10, 20, 0, 59, 63, 0, 8, 0]
Here's the output which I desire after moving all the Zero's to left:
output = [0, 0, 0, 1, 10, 20, 59, 63, 8]
Here's the code I tried:
class Solution:
def moveZeroes(self, nums):
c = 0
for i in range(len(nums)):
if nums[i] != 0:
nums[i], nums[c] = nums[c], nums[i]
c += 1
return nums
print(Solution().moveZeroes(nums))
This code gives me output as:
[1, 10, 20, 59, 63, 8, 0, 0, 0]
But my desired output is:
[0, 0, 0, 1, 10, 20, 59, 63, 8]
You can use sorted() with key as bool to achieve this as:
>>> nums = [1, 10, 20, 0, 59, 63, 0, 8, 0]
>>> sorted(nums, key=bool)
[0, 0, 0, 1, 10, 20, 59, 63, 8]
It will work for 0s. In order to make it more generic for any number, you can define key as lambda x: x!=left_num:
>>> left_num = 0
>>> sorted(nums, key=lambda x: x!=left_num)
[0, 0, 0, 1, 10, 20, 59, 63, 8]
As an alternative, here's a less Pythonic (but efficient) version of it using list.count():
>>> nums = [1, 10, 20, 0, 59, 63, 0, 8, 0]
>>> left_num = 0
>>> [left_num]*nums.count(left_num) + [n for n in nums if n!=left_num]
[0, 0, 0, 1, 10, 20, 59, 63, 8]
Here I am finding the count of zeroes in the list (say n), and assigning n zeroes in the start of new list. To get the rest of the unsorted list, I am using list comprehension to filter out the 0s from the list.
output = []
for i in nums:
if i == 0:
output.insert(0, 0)
else:
output.append(i)
output
sorted(nums, key=lambda i: i != 0)
# or
sorted(nums, key=bool)
you could filter out the zeros and insert the number of removed zeros at the begining of the output list:
nums = [1, 10, 20, 0, 59, 63, 0, 8, 0]
output = list(filter(None,nums))
output = [0]*(len(nums)-len(output)) + output
print(output)
# [0, 0, 0, 1, 10, 20, 59, 63, 8]
since the question does not mention any constraints, maybe this fine piece of one-line will help you:
def zero_left(list):
return [x for x in list if x == 0] + [x for x in list if x!=0]

Avoid for-loop to split array into multiple arrays by index values using numpy

Input: There are two input arrays:
value_array = [56, 10, 65, 37, 29, 14, 97, 46]
index_array = [ 0, 0, 1, 0, 3, 0, 1, 1]
Output: I want to split value_array using index_array without using for-loop. So the output array will be:
split_array = [[56, 10, 37, 14], # index 0
[65, 97, 46], # index 1
[], # index 2
[29]] # index 3
Is there any way to do that using numpy without using any for-loop? I have looked at numpy.where but cannot figure it out how to do that.
For-loop: Here is the way to do that using for-loop. I want to avoid for-loop.
split_array = []
for i in range(max(index_array) + 1):
split_array.append([])
for i in range(len(value_array)):
split_array[index_array[i]].append(value_array[i])
Does this suffice?
Solution 1 (Note: for loop is not over the entire index array)
import numpy as np
value_array = np.array([56, 10, 65, 37, 29, 14, 97, 46])
index_array = np.array([ 0, 0, 1, 0, 3, 0, 1, 1])
max_idx = np.max(index_array)
split_array = []
for idx in range(max_idx + 1):
split_array.append([])
split_array[-1].extend(list(value_array[np.where(index_array == idx)]))
print(split_array)
[[56, 10, 37, 14], [65, 97, 46], [], [29]]
Solution 2
import numpy as np
value_array = np.array([56, 10, 65, 37, 29, 14, 97, 46])
index_array = np.array([ 0, 0, 1, 0, 3, 0, 1, 1])
value_array = value_array[index_array.argsort()]
split_idxs = np.squeeze(np.argwhere(np.diff(np.sort(index_array)) != 0) + 1)
print(np.array_split(value_array, split_idxs))
[array([56, 10, 37, 14]), array([65, 97, 46]), array([29])]
Indeed, you can use numpy by using arrays :
import numpy as np
value_array=np.array(value_array)
index_array=np.array(index_array)
split_array=[value_array[np.where(index_array==j)[0]] for j in set(index_array)]
You could do:
import numpy as np
value_array = np.array([56, 10, 65, 37, 29, 14, 97, 46])
index_array = np.array([ 0, 0, 1, 0, 3, 0, 1, 1])
# find the unique values in index array and the corresponding counts
unique, counts = np.unique(index_array, return_counts=True)
# create an array with 0 for the missing indices
zeros = np.zeros(index_array.max() + 1, dtype=np.int32)
zeros[unique] = counts # zeros = [4 3 0 1] 0 -> 4, 1 -> 3, 2 -> 0, 3 -> 1
# group by index array
so = value_array[np.argsort(index_array)] # so = [56 10 37 14 65 97 46 29]
# finally split using the counts
res = np.split(so, zeros.cumsum()[:-1])
print(res)
Output
[array([56, 10, 37, 14]), array([65, 97, 46]), array([], dtype=int64), array([29])]
The time complexity of this approach is O(N logN).
Additionally if you don't care about the missing indices, you could use the following:
_, counts = np.unique(index_array, return_counts=True)
res = np.split(value_array[np.argsort(index_array)], counts.cumsum()[:-1])
print(res)
Output
[array([56, 10, 37, 14]), array([65, 97, 46]), array([29])]

Is there a way to conditionally index 3D-numpy array?

Having an array A with the shape (2,6, 60), is it possible to index it based on a binary array B of shape (6,)?
The 6 and 60 is quite arbitrary, they are simply the 2D data I wish to access.
The underlying thing I am trying to do is to calculate two variants of the 2D data (in this case, (6,60)) and then efficiently select the ones with the lowest total sum - that is where the binary (6,) array comes from.
Example: For B = [1,0,1,0,1,0] what I wish to receive is equal to stacking
A[1,0,:]
A[0,1,:]
A[1,2,:]
A[0,3,:]
A[1,4,:]
A[0,5,:]
but I would like to do it by direct indexing and not a for-loop.
I have tried A[B], A[:,B,:], A[B,:,:] A[:,:,B] with none of them providing the desired (6,60) matrix.
import numpy as np
A = np.array([[4, 4, 4, 4, 4, 4], [1, 1, 1, 1, 1, 1]])
A = np.atleast_3d(A)
A = np.tile(A, (1,1,60)
B = np.array([1, 0, 1, 0, 1, 0])
A[B]
Expected results are a (6,60) array containing the elements from A as described above, the received is either (2,6,60) or (6,6,60).
Thank you in advance,
Linus
You can generate a range of the indices you want to iterate over, in your case from 0 to 5:
count = A.shape[1]
indices = np.arange(count) # np.arange(6) for your particular case
>>> print(indices)
array([0, 1, 2, 3, 4, 5])
And then you can use that to do your advanced indexing:
result_array = A[B[indices], indices, :]
If you always use the full range from 0 to length - 1 (i.e. 0 to 5 in your case) of the second axis of A in increasing order, you can simplify that to:
result_array = A[B, indices, :]
# or the ugly result_array = A[B, np.arange(A.shape[1]), :]
Or even this if it's always 6:
result_array = A[B, np.arange(6), :]
An alternative solution using np.take_along_axis (from version 1.15 - docs)
import numpy as np
x = np.arange(2*6*6).reshape((2,6,6))
m = np.zeros(6, int)
m[0] = 1
#example: [1, 0, 0, 0, 0, 0]
np.take_along_axis(x, m[None, :, None], 0) #add dimensions to mask to match array dimensions
>>array([[[36, 37, 38, 39, 40, 41],
[ 6, 7, 8, 9, 10, 11],
[12, 13, 14, 15, 16, 17],
[18, 19, 20, 21, 22, 23],
[24, 25, 26, 27, 28, 29],
[30, 31, 32, 33, 34, 35]]])

element wise checking of nested list

Check nested list elementwise for multiple conditions and return 0 or 1 if condition is not met or is met.
I have to check
at least 14
cannot be equal to= 19
if the element ends in 4 or 9
For example, age array of
[[22, 13, 31, 13],
[17, 14, 24, 22]]
will have the output array as
[[0, 0, 0, 0],
[0, 1, 1, 0]]
I've tried flattening the list and then checking each condition, but it's not working.
flat_list = [item for sublist in age for item in sublist]
x=14
[not x for x in flat_list]
There's a faster numpy solution:
((arr >= 14) & (arr != 19) & ((arr%10 == 4) | (arr%10==9))).astype(int)
Code:
import numpy as np
arr = np.array([[22, 13, 31, 13],
[17, 14, 24, 22]])
print(((arr >= 14) & (arr != 19) & ((arr%10 == 4) | (arr%10==9))).astype(int))
# [[0 0 0 0]
# [0 1 1 0]]
You could do it with list comprehensions like so:
somelist = [[22, 13, 31, 13],
[17, 14, 24, 22]]
result = [[1 if (x%10==4 or x%10==9) and (x>=14 and x!=19) else 0 for x in sublist] for sublist in somelist]
result
[[0, 0, 0, 0], [0, 1, 1, 0]]
Where x%10 will get the last digit in each number, allowing the direct comparison. By grouping the two conditions, you can more logically lay out what you want to do, though this gets a bit messy for a list comprehension.
A better way (at the potential expense of speed) might be to use map:
def check_num(num):
value_check = num >= 14 and num != 19
last_num_check = num % 10 == 4 or num % 10 == 9
return int(value_check and last_num_check)
somelist = [[22, 13, 31, 13],
[17, 14, 24, 22]]
result = [[x for x in map(check_num, sublist)] for sublist in somelist]
result
[[0, 0, 0, 0], [0, 1, 1, 0]]
Timing the difference between operations:
List Comprehension
python -m timeit -s 'somelist = [[22, 13, 31, 13], [17, 14, 24, 22]]' '[[1 if (x%10==4 or x%10==9) and (x>=14 and x!=19) else 0 for x in sublist] for sublist in somelist]'
1000000 loops, best of 3: 1.35 usec per loop
Map
python -m timeit -s 'from somefunc import check_num; somelist = [[22, 13, 31, 13], [17, 14, 24, 22]]' '[[x for x in map(check_num, sublist)] for sublist in somelist]'
100000 loops, best of 3: 3.37 usec per loop
C.Nvis has a good answer with list comprehension. You can also solve this using nested for loops
def is_valid(x):
return (x == 14) or (x%10 == 4) or (x%10 == 9)
out = []
for sublist in matrix:
out_sublist = []
for i in sublist:
if (is_valid(i)):
out_sublist.append(1)
else:
out_sublist.append(0)
out.append(out_sublist)
print(out)
These answers are effectively the same algorithm.
Just for your example it can be done with a bit of mapping.
The way you can verify that the last digit equals to a number is by applying a modulo-10 on the number.
my_list = [[22, 13, 31, 13],[17, 14, 24, 22]]
result_list = []
for sublist in my_list:
result_list.append(list(map(lambda x: 1 if x % 10 == 4 and x >= 14 and x != 19 else 0, sublist)))
print(result_list)
will yield:
[[0, 0, 0, 0], [0, 1, 1, 0]]

Python with numpy: How to delete an element from each row of a 2-D array according to a specific index

Say I have a 2-D numpy array A of size 20 x 10.
I also have an array of length 20, del_ind.
I want to delete an element from each row of A according to del_ind, to get a resultant array of size 20 x 9.
How can I do this?
I looked into np.delete with a specified axis = 1, but this only deletes element from the same position for each row.
Thanks for the help
You will probably have to build a new array.
Fortunately you can avoid python loops for this task, using fancy indexing:
h, w = 20, 10
A = np.arange(h*w).reshape(h, w)
del_ind = np.random.randint(0, w, size=h)
mask = np.ones((h,w), dtype=bool)
mask[range(h), del_ind] = False
A_ = A[mask].reshape(h, w-1)
Demo with a smaller dataset:
>>> h, w = 5, 4
>>> %paste
A = np.arange(h*w).reshape(h, w)
del_ind = np.random.randint(0, w, size=h)
mask = np.ones((h,w), dtype=bool)
mask[range(h), del_ind] = False
A_ = A[mask].reshape(h, w-1)
## -- End pasted text --
>>> A
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11],
[12, 13, 14, 15],
[16, 17, 18, 19]])
>>> del_ind
array([2, 2, 1, 1, 0])
>>> A_
array([[ 0, 1, 3],
[ 4, 5, 7],
[ 8, 10, 11],
[12, 14, 15],
[17, 18, 19]])
Numpy isn't known for inplace edits; it's mainly intended for statically sized matrices. For that reason, I'd recommend doing this by copying the intended elements to a new array.
Assuming that it's sufficient to delete one column from every row:
def remove_indices(arr, indices):
result = np.empty((arr.shape[0], arr.shape[1] - 1))
for i, (delete_index, row) in enumerate(zip(indices, arr)):
result[i] = np.delete(row, delete_index)
return result

Categories