replace numbers from one list to another - python

strong text
I have matrix (3-d array)
strong text
"a" and "b" shape: (5, 3, depths), depths == can varaible, in this example is 2, but sometimes could be 3 or 4 or 6, I am looking for a function that works with different depths.
Blockquote
a= [[[10,15,10,9,45], [2,21,78,14,96], [2,2,78,14,96], [3,34,52,87,21], [52,14,45,85,74] ], [[52,14,45,85,74], [2,2,78,14,96], [15,41,48,48,74], [3,34,52,87,21], [14,71,84,85,41]]]
Blockquote
b= [[[0,1,0,1,1], [2,2,1,0,0], [2,2,1,1,0], [0,0,0,0,1], [0,0,1,1,1] ], [[0,0,0,1,1], [0,1,1,1,2], [2,2,2,2,0], [0,0,0,1,1], [1,0,0,0,1]]]
strong text
I want a matrix "c", "c" should be the copy of "a", but when a value in "b" is == 0, "c" will also be == 0
Blockquote
c= [[[0,15,0,9,45], [2,21,78,0,0], [2,21,78,14,0], [0,0,0,0,21], [0,0,45,85,74] ], [[0,0,0,85,74], [0,2,78,14,96], [15,41,48,48,0], [0,0,0,87,21], [14,0,0,0,41]]]
strong text
thank for yourl help

Use numpy arrays and element-wise multiplication:
import numpy as np
a= [[[10,15,10,9,45], [2,21,78,14,96], [2,2,78,14,96], [3,34,52,87,21], [52,14,45,85,74] ], [[52,14,45,85,74], [2,2,78,14,96], [15,41,48,48,74], [3,34,52,87,21], [14,71,84,85,41]]]
b= [[[0,1,0,1,1], [2,2,1,0,0], [2,2,1,1,0], [0,0,0,0,1], [0,0,1,1,1] ], [[0,0,0,1,1], [0,1,1,1,2], [2,2,2,2,0], [0,0,0,1,1], [1,0,0,0,1]]]
result = np.array(a) * np.array(b)
result = result.tolist() # if you want the final result as a list
print(result)
[[[0, 15, 0, 9, 45], [4, 42, 78, 0, 0], [4, 4, 78, 14, 0], [0, 0, 0, 0, 21], [0, 0, 45, 85, 74]], [[0, 0, 0, 85, 74], [0, 2, 78, 14, 192], [30, 82, 96, 96, 0], [0, 0, 0, 87, 21], [14, 0, 0, 0, 41]]]

Note : In the question, you are talking about 5x3 blocks, but your example is 5x5. i'll assume the correct format is 5x5xDepth
Without using numpy
Let's define our depth and a result blockquote
depth = len(a)
result = []
So we can iterate trought our blockquote :
for x in range(depth):
# This is a 5 x 5 array
2d_block = []
for y in range(5):
# This is our final dimension, array of lengh 5
1d_block = []
for z in range(5):
# Check if b is 0
if b[x][y][z] == 0:
1d_block.append(0)
else:
1d_block.append(a[x][y][z])
# Add our block to the current 2D block
2d_block.append(1d_block)
# Add our blocks to the result
result.append(2d_block)
Recursive alternative
A more advanced solution
def convert_list(a, b):
if isinstance(a, list):
# Recursive call on all sub list
return [convert_list(a[i], b[i]) for i in range(len(a))]
else
# When we find an element, return a if b is not 0
return a if b!=0 else 0
This is a recursive function so you don't need to mind about the dimensions of your blockquote (as look as a and b have the same lengh)
Using numpy
Inspired by msamsami anwser, with a step to convert all non-zero numbers in b to 1 to avoid multiplying the result (zeros stay 0 so we can filter a values)
# Convert b to an array with 0 and 1, to avoid multiplying by 2
def toBinary(item):
if isinstance(item, list):
return [toBinary(x) for x in item]
else:
return item != 0
filter = toBinary(b)
result = np.array(a) * np.array(filter)

Related

Given a list of integers, split it into 2 list that return True if it is equal, else, return False (If it is impossible)

Same as before, I am given a list of string, and I have to split that ONE list into two list to check if it is possible to have equal sum. If possible, return array 1 and array 2.
The following code works for ONE set of combination
Eg. myArr = [1, 2, 3, 4]
Each full iteration will do the following (targetSum = sum(myArr) / 2) #5
#1: PartialArr1 = [1] , PartialArr2 = [2,3,4] , Check if Arr1 == targetSum
#1: PartialArr1 = [1, 2] , PartialArr2 = [3,4] , Check if Arr1 == targetSum
#1: PartialArr1 = [1, 2, 3] , PartialArr2 = [4] , Check if Arr1 == targetSum
#1: PartialArr1 = [1, 2, 3, 4] , PartialArr2 = [] , Check if Arr1 == targetSum
While #1 does not returns any True value, permutate the number ONCE
#2: myArr = [2, 3, 4, 1]
#2: PartialArr1 = [2] , PartialArr2 = [3,4,1] , Check if Arr1 == targetSum
#2: PartialArr1 = [2, 3] , PartialArr2 = [4,1] , Check if Arr1 == targetSum #Return PartialArr1, PartialArr2
def group(s):
sumOfArr = sum(s)
isPossible = sumOfArr%2
if(isPossible == 0):
#No remainder, possible to get a combination of two equal arrays
targetSum = int(sumOfArr/2)
partialArr1 = []
partialArr2 = []
i = 0
while(targetSum != sum(partialArr1)):
partialArr1.append(s[i])
partialArr2 = s[i+1:]
i+=1
if(i == len(s)-1) or (sum(partialArr1) > targetSum):
partialArr1 = []
partialArr2 = []
i = 0
s = s[1:] + s[:1]
return partialArr1,partialArr2
else:
#Not possible, return None, None
return None, None
While my solution works for most permutation group, it doesn't work on the following:
#The following works with my code ->
#s = [135, 129, 141, 121, 105, 109, 105, 147]
#s = [-14, 3, 4, 13, -1, -5, 0, 5, -10, 8, -4, 10, -12, 11, 9, 12, -6, -11, -9, -8]
#s= [-1, 1, 4, 2, 8, 0]
#s = [10, 2,2,10, 2, 2, 2,10]
#SOLUTION SHOULD RETURN THE FOLLOWING
#s1 = [6, 10, 6, 10], sum(s1) = 32
#s2 = [7, -3, 1, -4, 2, 2, 7, -3, 2, 4, 0, 7, 6, -2, -4, 10], sum(s1) = 32
s = [7, -3, 6, 1, 10, -4, 2, 2, 6, 7, -3, 2, 4, 0, 7, 6, -2, 10, -4, 10]
group(s) #This does not work because it does not permutate all possible ways of len(s)^2 ways. Thus I am stucked at this point of time
Any help will be appreciated!! Thanks!!
From what I understood, you need to see if your array can be divided into 2 equal arrays and return True if it does else you return False, you don't need the arrays it gets divided to, just the fact that it can be divided.
Well if it your array can be divided into 2 equal arrays, some of its elements should sum to the half of the total sum of your array.
For example :
[5,3,5,2,10,1]
This array has the sum of 26. if some of its elements can be equal to 13 then it can be divided into 2 arrays of equal arrays, because the rest of the elements would sum to 13 too. example : [10,3] and [5,5,2,1], so you return True when you find that a combination of elements of your array equals half the sum of the total array. Also automatically an array with an odd sum is not dividable to 2 equal arrays. nb : searching for all combinations has a high complexity and will be slow with big lists.
This is what exactly i am doing, using itertools to get the combinations that sum to the half the total sum of the array, if there are combinations then it can be divided to 2 equal arrays, test it :
import itertools
numbers = [135, 129, 141, 121, 105, 109, 105, 147]
if((sum(numbers)%2)!=0):
print(False)
else:
result = [seq for i in range(len(numbers), 0, -1) for seq in itertools.combinations(numbers, i) if sum(seq) == (sum(numbers)//2)]
if len(result)>1:
print(True)
else:
print(False)
Here is the function that you need, it returns True when the array can be divided into 2 equal arrays and False if it cannot, without use of any libraries :
def search(numbers):
target = sum(numbers)/2
def subset_sum(numbers, target, partial=[]):
s = sum(partial)
if s == target:
print(5/0)
if s >= target:
return
for i in range(len(numbers)):
n = numbers[i]
remaining = numbers[i + 1:]
subset_sum(remaining, target, partial + [n])
try:
subset_sum(numbers, target)
return False
except:
return True
numbers = [135, 129, 141, 121, 105, 109, 105, 147]
print(search(numbers))

Form an numpy array from indices and fill with zeroes

I have a nympy array a = np.array([483, 39, 18, 999, 20, 48]
I have an array of indices indices = np.array([2, 3])
I would like to have all the indices of the array and fill the rest of the indices with 0 so I get as a result :
np.array([0, 0, 18, 999, 0, 0])
Thank you for your answer.
Create an all zeros array and copy the values at the desired indices:
import numpy as np
a = np.array([483, 39, 18, 999, 20, 48])
indices = np.array([2, 3])
b = np.zeros_like(a)
b[indices] = a[indices]
# a = b # if needed
print(a)
print(indices)
print(b)
Output:
[483 39 18 999 20 48]
[2 3]
[ 0 0 18 999 0 0]
Hope that helps!
----------------------------------------
System information
----------------------------------------
Platform: Windows-10-10.0.16299-SP0
Python: 3.8.1
NumPy: 1.18.1
----------------------------------------
EDIT: Even better, use np.setdiff1d:
import numpy as np
a = np.array([483, 39, 18, 999, 20, 48])
indices = np.array([2, 3])
print(a)
print(indices)
a[np.setdiff1d(np.arange(a.shape[0]), indices, True)] = 0
print(a)
Output:
[483 39 18 999 20 48]
[2 3]
[ 0 0 18 999 0 0]
What about using list comprehension?
a = np.array([n if i in indices else 0 for i, n in enumerate(a)])
print(a) #array([ 0, 0, 18, 999, 0, 0])
You can create a function that uses the input array and the index array to do this, as in the following:
import numpy as np
def remove_by_index(input_array, indexes):
for i,_ in enumerate(input_array):
if i not in indexes:
input_array[i] = 0
return input_array
input_array = np.array([483, 39, 18, 999, 20, 48])
indexes = np.array([2, 3])
new_out = remove_by_index(input_array, indexes)
expected_out = np.array([0, 0, 18, 999, 0, 0])
print(new_out == expected_out) # to check if it's correct
Edit
You can also use list comprehension inside the function, which would be better, as:
def remove_by_index(input_array, indexes):
return [input_array[i] if (i in indexes) else 0 for i,_ in enumerate(input_array)]
It is not, as pointed out in comments, the most efficient way of doing it, performing iteration at Python level instead of C level, but it does work, and for casual use it will solve.

How to filter Python list while keeping filtered values zero

With
input = [0,0,5,9,0,4,10,3,0]
as list
I need an output, which is going to be two highest values in input while setting other list elements to zero.
output = [0,0,0,9,0,0,10,0,0]
The closest I got:
from itertools import compress
import numpy as np
import operator
input= [0,0,5,9,0,4,10,3,0]
top_2_idx = np.argsort(test)[-2:]
test[top_2_idx[0]]
test[top_2_idx[1]]
Can you please help?
You can sort, find the two largest values, and then use a list comprehension:
input = [0,0,5,9,0,4,10,3,0]
*_, c1, c2 = sorted(input)
result = [0 if i not in {c1, c2} else i for i in input]
Output:
[0, 0, 0, 9, 0, 0, 10, 0, 0]
Not as pretty as Ajax's solution but a O(n) solution and a little more dynamic:
from collections import deque
def zero_non_max(lst, keep_top_n):
"""
Returns a list with all numbers zeroed out
except the keep_top_n.
>>> zero_non_max([0, 0, 5, 9, 0, 4, 10, 3, 0], 3)
>>> [0, 0, 5, 9, 0, 0, 10, 0, 0]
"""
lst = lst.copy()
top_n = deque(maxlen=keep_top_n)
for index, x in enumerate(lst):
if len(top_n) < top_n.maxlen or x > top_n[-1][0]:
top_n.append((x, index))
lst[index] = 0
for val, index in top_n:
lst[index] = val
return lst
lst = [0, 0, 5, 9, 0, 4, 10, 3, 0]
print(zero_non_max(lst, 2))
Output:
[0, 0, 0, 9, 0, 0, 10, 0, 0]
Pure numpy approach:
import numpy as np
arr = np.array([0, 0, 5, 9, 0, 4, 10, 3, 0])
top_2_idx = np.argsort(arr)[-2:]
np.put(arr, np.argwhere(~np.isin(arr, arr[top_2_idx])), 0)
print(arr)
The output:
[ 0 0 0 9 0 0 10 0 0]
Numpy.put
It's possible to achieve this with a single list traversal, making the algorithm O(n):
First find the two highest values with a single traversal;
Then create a list of zeros and add in the found maxima.
Code
def two_max(lst):
# Find two highest values in a single traversal
max_i, max_j = 0, 1
for i in range(len(lst)):
_, max_i, max_j = sorted((max_i, max_j, i), key=lst.__getitem__)
# Make a new list with zeros and replace both maxima
new_lst = [0] * len(lst)
new_lst[max_i], new_lst[max_j] = lst[max_i], lst[max_j]
return new_lst
lst = [0, 0, 5, 9, 0, 4, 10, 3, 0]
print(two_max(lst)) # [0, 0, 0, 9, 0, 0, 10, 0, 0]
Note that if the maximum value in the list appears more than twice, only the two left-most values will appear.
As a sidenote, do not use names such as input in your code as this overshadows the built-in function of the same name.
Here is another numpy-based solution that avoids sorting the entire array, which takes O(nlogn) time.
import numpy as np
arr = np.array([0,0,5,9,0,4,10,3,0])
arr[np.argpartition(arr,-2)[:-2]] = 0
If you want to create a new array as output:
result = np.zeros_like(arr)
idx = np.argpartition(arr,-2)[-2:]
result[idx] = arr[idx]
A corresponding Python-native solution is to use heap.nlargest, which also avoids sorting the entire array.
import heapq
arr = [0,0,5,9,0,4,10,3,0]
l = len(arr)
idx1, idx2 = heapq.nlargest(2, range(l), key=arr.__getitem__)
result = [0] * l
result[idx1] = arr[idx1]
result[idx2] = arr[idx2]

Python: Add value to a list at predefined indexes

I have a list (aList)
I need to create another list of the same length as aList (checkColumn) and add 1 at the positions defined in the index list (indexList), the positions not defined in the indexList should be 0
Input:
aList = [70, 2, 4, 45, 7 , 55, 61, 45]
indexList = [0, 5, 1]
Desired Output:
checkColumn = [1,1,0,0,0,1]
I have been experimenting around with the following code, but I get the output as [1,1]
for a in len(aList):
if a == indexList[a]:
checkColumn = checkColumn[:a] + [1] + checkColumn[a:]
else:
checkColumn = checkColumn[:a] + [0] + checkColumn[a:]
I have tried with checkColumn.insert(a, 1) and I get the same result.
Thanks for the help!
Would this help?
First, you initialize checkColumn with 0
checkColumn = [0] * len(aList)
Then, loop through indexList and update the checkColumn
for idx in indexList:
checkColumn[idx] = 1
Cheers!
You could do this in one line using a list comprehension:
aList = [70, 2, 4, 45, 7 , 55, 61, 45]
indexList = [0, 5, 1]
checkColumn = [1 if a in indexList else 0 for a in range(len(aList))]
print(checkColumn)
# [1, 1, 0, 0, 0, 1, 0, 0]

Replace values in subarray based upon dynamic condition in Numpy

I have a Python Numpy array that is a 2D array where the second dimension is a subarray of 3 elements of integers. For example:
[ [2, 3, 4], [9, 8, 7], ... [15, 14, 16] ]
For each subarray I want to replace the lowest number with a 1 and all other numbers with a 0. So the desired output from the above example would be:
[ [1, 0, 0], [0, 0, 1], ... [0, 1, 0] ]
This is a large array, so I want to exploit Numpy performance. I know about using conditions to operate on array elements, but how do I do this when the condition is dynamic? In this instance the condition needs to be something like:
newarray = (a == min(a)).astype(int)
But how do I do this across each subarray?
You can specify the axis parameter to calculate a 2d array of mins(if you keep the dimension of the result), then when you do a == a.minbyrow, you will get trues at the minimum position for each sub array:
(a == a.min(1, keepdims=True)).astype(int)
#array([[1, 0, 0],
# [0, 0, 1],
# [0, 1, 0]])
How about this?
import numpy as np
a = np.random.random((4,3))
i = np.argmin(a, axis=-1)
out = np.zeros(a.shape, int)
out[np.arange(out.shape[0]), i] = 1
print(a)
print(out)
Sample output:
# [[ 0.58321885 0.18757452 0.92700724]
# [ 0.58082897 0.12929637 0.96686648]
# [ 0.26037634 0.55997658 0.29486454]
# [ 0.60398426 0.72253012 0.22812904]]
# [[0 1 0]
# [0 1 0]
# [1 0 0]
# [0 0 1]]
It appears to be marginally faster than the direct approach:
from timeit import timeit
def dense():
return (a == a.min(1, keepdims=True)).astype(int)
def sparse():
i = np.argmin(a, axis=-1)
out = np.zeros(a.shape, int)
out[np.arange(out.shape[0]), i] = 1
return out
for shp in ((4,3), (10000,3), (100,10), (100000,1000)):
a = np.random.random(shp)
d = timeit(dense, number=40)/40
s = timeit(sparse, number=40)/40
print('shape, dense, sparse, ratio', '({:6d},{:6d}) {:9.6g} {:9.6g} {:9.6g}'.format(*shp, d, s, d/s))
Sample run:
# shape, dense, sparse, ratio ( 4, 3) 4.22172e-06 3.1274e-06 1.34992
# shape, dense, sparse, ratio ( 10000, 3) 0.000332396 0.000245348 1.35479
# shape, dense, sparse, ratio ( 100, 10) 9.8944e-06 5.63165e-06 1.75693
# shape, dense, sparse, ratio (100000, 1000) 0.344177 0.189913 1.81229

Categories