Compare and multiply elements in a list - python

I'm trying to write in an algorithm a function that:
Check if all elements in a list are different
Multiply all elements in the list, except the zeros
But I can't find a way to compare all elements in one list, do you have any idea ?
Thanks!
PS: I use arr = np.random.randint(10, size=a) to create a random list
EDIT:
More precisely, I'm trying to check if, in a numpy array to be more precise, all the elements are the same or different, if they are different, that it returns me True.
Also, once that done, multiply all elements in the array except the zeros
For example:
If I have an array [4,2,6,8,9,0], the algorithm tells returns me at first True because all elements are different, then it multiplies them 4*2*6*8*9 except the 0

To check if all elements in a list are different you can convert the list into a set which removes duplicates and compare the length of the set to the original list. If the length of the set is different than the length of the list, then there are duplicates.
x = np.random.randint(10, size=10)
len(set(x)) == len(x)
To multiply all values except 0 you can do list comprehension to remove the 0s and use np.prod to multiply the values in the new list.
np.prod([i for i in x if i != 0])
Example:
x = np.random.randint(10, size=10)
if len(set(x)) == len(x):
print(np.prod([i for i in x if i != 0]))
else:
print("array is not unique")

You can use numpy.unique.
Following code snippet checks if all elements in the array are unique (different from each other) and if so, it will multiply non-zero values with factor factor:
import numpy as np
factor = 5
if np.unique(arr).size == arr.size:
arr[arr != 0] = arr[arr != 0] * factor

You can use Collections to find the unique numbers. I have included a code that solves your problem.
import numpy as np
from collections import Counter
a = 5
arr = np.random.randint(10, size=a)
result = 1 #Variable that will store the product
flag = 0 #The counter
#Check if all the numbers are unique
for i in Counter(arr):
if Counter(arr)[i] > 1:
flag = 1
break
#Convert the dictionary into a list
l = [i for i in Counter(arr)]
#Return the product of all the numbers in the list except 0
if flag == 0:
for i in l:
if i != 0:
result = result * i
else:
print("The numbers are not unique")

Just for fun, here's a one-liner:
arr = np.array([1, 2, 3, 4, 0])
np.prod(arr[arr!=0]) if np.unique(arr).size == arr.size else False
>>> 24
If the array is [1, 2, 3, 4, 4] the result is False

Related

find top_k element of numpy ndarray and ignore zero

Given a numpy ndarray like the following
x = [[4.,0.,2.,0.,8.],
[1.,3.,0.,9.,5.],
[0.,0.,4.,0.,1.]]
I want to find the indices of the top k (e.g. k=3) elements of each row, excluding 0, if possible. If there are less than k positive elements, then just return their indices (in a sorted way).
The result should look like (a list of array)
res = [[4, 0, 2],
[3, 4, 1],
[2, 4]]
or just one flatten array
res = [4,0,2,3,4,2,2,4]
I know argsort can find the indices of top k elements in a sorted order. But I am not sure how to filter out the 0.
You can use numpy.argsort with (-num) for getting index as descending. then use numpy.take_along_axis for getting values base index of 2D sorted.
Because you want to ignore zero you can insert zero for other columns after three (as you mention in the question). At the end return value from the sorted values that is not zero.
x = np.array([[4.,0.,2.,0.,8.],[1.,3.,0.,9.,5.],[0.,0.,4.,0.,1.]])
idx_srt = np.argsort(-x)
val_srt = np.take_along_axis(x, idx_srt, axis=-1)
val_srt[:, 3:] = 0
res = idx_srt[val_srt!=0]
print(res)
[4 0 2 3 4 1 2 4]
Try one of these two:
k = 3
res = [sorted(range(len(r)), key=(lambda i: r[i]), reverse=True)[:min(k, len([n for n in r if n > 0]))] for r in x]
or
res1 = [np.argsort(r)[::-1][:min(k, len([n for n in r if n > 0]))] for r in x]
I came up with the following solution:
top_index = score.argsort(axis=1) # score here is my x
positive = (score > 0).sum(axis=1)
positive = np.minimum(positive, k) # top k
# broadcasting trick to get mask matrix that selects top k (k = min(2000, num of positive scores))
r = np.arange(score.shape[1])
mask = (positive[:,None] > r)
top_index_flatten = top_index[:, ::-1][mask]
I compare my result with the one suggested by #I'mahdi and they are consistent.

Find missing elements in a list created from a sequence of consecutive integers with duplicates in O(n)

This is a Find All Numbers Disappeared in an Array problem from LeetCode:
Given an array of integers where 1 ≤ a[i] ≤ n (n = size of array),
some elements appear twice and others appear once.
Find all the elements of [1, n] inclusive that do not appear in this array.
Could you do it without extra space and in O(n) runtime? You may
assume the returned list does not count as extra space.
Example:
Input:
[4,3,2,7,8,2,3,1]
Output:
[5,6]
My code is below - I think its O(N) but interviewer disagrees
def findDisappearedNumbers(self, nums: List[int]) -> List[int]:
results_list=[]
for i in range(1,len(nums)+1):
if i not in nums:
results_list.append(i)
return results_list
You can implement an algorithm where you loop through each element of the list and set each element at index i to a negative integer if the list contains the element i as one of the values,. You can then add each index i which is positive to your list of missing items. It doesn't take any additional space and uses at the most 3 for loops(not nested), which makes the complexity O(3*n), which is basically O(n). This site explains it much better and also provides the source code.
edit- I have added the code in case someone wants it:
#The input list and the output list
input = [4, 5, 3, 3, 1, 7, 10, 4, 5, 3]
missing_elements = []
#Loop through each element i and set input[i - 1] to -input[i - 1]. abs() is necessary for
#this or it shows an error
for i in input:
if(input[abs(i) - 1] > 0):
input[abs(i) - 1] = -input[abs(i) - 1]
#Loop through the list again and append each positive value to output list
for i in range(0, len(input)):
if input[i] > 0:
missing_elements.append(i + 1)
For me using loops is not the best way to do it because loops increase the complexity of the given problem. You can try doing it with sets.
def findMissingNums(input_arr):
max_num = max(input_arr) # get max number from input list/array
input_set = set(input_arr) # convert input array into a set
set_num = set(range(1,max(input_arr)+1)) #create a set of all num from 1 to n (n is the max from the input array)
missing_nums = list(set_num - input_set) # take difference of both sets and convert to list/array
return missing_nums
input_arr = [4,3,2,7,8,2,3,1] # 1 <= input_arr[i] <= n
print(findMissingNums(input_arr)) # outputs [5 , 6]```
Use hash table, or dictionary in Python:
def findDisappearedNumbers(self, nums):
hash_table={}
for i in range(1,len(nums)+1):
hash_table[i] = False
for num in nums:
hash_table[num] = True
for i in range(1,len(nums)+1):
if not hash_table[i]:
print("missing..",i)
Try the following :
a=input() #[4,3,2,7,8,2,3,1]
b=[x for x in range(1,len(a)+1)]
c,d=set(a),set(b)
print(list(d-c))

How to find the most frequent progressive digit from a list of 4-digits numbers

I am quite new in Python programming. What's an efficient and Pyhtonic way to find the most frequent progressive digit from a list of 4-digits numbers?
Let's say I have the following list: [6111, 7111, 6112, 6121, 6115, 6123].
The logic is to observe that the for the first digit the 6 is the most frequent. I can eliminate the number 7111 for the next considerations.
For the second digit I consider the new candidates [6111, 6112, 6121, 6115, 6123] and I observe that the 1 is the most frequent digit and so on.
At the end of the algorithm I'll have just 1 number of the list left.
If there are 2 or more number with the same occurrences for a digit I can either pick the smaller one on a random one between all of them.
A simple approach could be to convert the list into a Nx4 matrix and consider for each column the most frequent digit. This could work but I find a very stupid and inefficient way to solve this problem. Can anyone help?
EDIT: my code for this solution (NOTE: THIS CODE DOES NOT ALWAYS WORK, SOMETHING IS WRONG. FOR THE SOLUTION TO THIS PROBLEM PLEASE REFER TO #MadPhysicist ANSWER)
import numpy as np
import pandas as pd
from collections import Counter
numbers_list = [6111, 7111, 6112, 6121, 6115, 6123]
my_list = []
for number in numbers_list:
digit_list = []
for c in str(number):
digit_list.append(c)
my_list.append(digit_list)
matrix = np.array(my_list)
matrix0 = matrix
my_counter = Counter(matrix.T[0]).most_common(1)
i=0
for digit0 in matrix.T[0]:
if digit0 != my_counter[0][0]:
matrix0 = np.delete(matrix, i, 0)
i += 1
matrix = matrix0
matrix1 = matrix
my_counter = Counter(matrix.T[1]).most_common(1)
i=0
for digit1 in matrix.T[1]:
if digit1 != my_counter[0][0]:
matrix1 = np.delete(matrix, i, 0)
i += 1
matrix = matrix1
matrix2 = matrix
my_counter = Counter(matrix.T[2]).most_common(1)
i=0
for digit2 in matrix.T[2]:
if digit2 != my_counter[0][0]:
matrix2 = np.delete(matrix, i, 0)
i += 1
matrix = matrix2
matrix3 = matrix
my_counter = Counter(matrix.T[3]).most_common(1)
i=0
for digit3 in matrix.T[3]:
if digit3 != my_counter[0][0]:
matrix3 = np.delete(matrix, i, 0)
i += 1
matrix = matrix3
print (matrix[0])
Your idea of converting to a numpy array is solid. You don't need to split it up-front. A series of masks and histograms will pare down the array fairly quickly.
z = np.array([6111, 7111, 6112, 6121, 6115, 6123])
The nth digits (zero-based) can be obtained with something like
nth = (z // 10**n) % 10
Counting the most frequent one can be accomplished quickly with np.bincount as shown here:
frequentest = np.argmax(np.bincount(nth))
You can select the elements that have that digit in the nth place with simply
mask = nth == frequentest
So now run this in a loop over n (going backwards):
# Input array
z = np.array([6111, 7111, 6112, 6121, 6115, 6123])
# Compute the maximum number of decimal digits in the list.
# You can just manually set this to 4 if you prefer
n = int(np.ceil(np.log10(z + 1).max()))
# Empty output array
output = np.empty(n, dtype=int)
# Loop over the number of digits in reverse.
# In this case, i will be 3, 2, 1, 0.
for i in range(n - 1, -1, -1):
# Get the ith digit from each element of z
# The operators //, ** and % are vectorized: they operate
# on each element of an array to return an array
ith = (z // 10**i) % 10
# Count the number of occurrences of each number 0-9 in the ith digit
# Bincount returns an array of 10 elements. counts[0] is the number of 0s,
# counts[1] is the number of 1s, ..., counts[9] is the number of 9s
counts = np.bincount(ith)
# argmax finds the index of the maximum element: the digit with the
# highest count
output[i] = np.argmax(counts)
# Trim down the array to numbers that have the requested digit in the
# right place. ith == output[i] is a boolean mask. It is 1 where ith
# is the most common digit and 0 where it is not. Indexing with such a
# mask selects the elements at locations that are non-zero.
z = z[ith == output[i]]
As it happens, np.argmax will return the index of the first maximum count if there are multiple available, meaning that it will always select the smallest number.
You can recover the number from output with something like
>>> output
array([1, 1, 1, 6])
>>> (output * 10**np.arange(output.size)).sum()
6111
You can also just get the remaining element of z:
>>> z[0]
6111

Return summation of two lists as elements in a new list

I have two lists for example, [5, 6, 3] and [ 5, 7] and I want to return [6,2,0] which is basically 563+57 = 620 where each element is returned in the new list. In case of carryover, I shall get a bigger list.
I am able to do it with the following approach in python:
a = [5,6,3]
b = [5,7]
str_a = ''.join(map(str, a))
str_b = ''.join(map(str, b))
int(str_a)+int(str_b)
lst = [int(i) for i in str(620)]
lst
It can be extended to multiple lists and looping through the lists. However, can it be done by looping through each elements in the given lists? Is that a prefered method compared to map and join string?
PS: I quickly got some down votes when I posted it. Sorry if I was not clear and I hope it is clear now.
Thanks for your help.
I got requested result with below codes, another approach than what Marcos provided and I was able to find as well. Only additional thing is you need is to pad arrays to maximum length + 1 with numpy pad.
def elementsum2array(arr1, arr2):
'''
1. Make the arrays of same length padding at the beginning/left side with 0's
so that arrays get the same same length of "maximum length + 1" (for carry at the end).
2. Get a new array of length "maximum length + 1".
3. Sum the elements of the arrays from last index to first index together with carry.
4. Fill the new array element with sum % 10.
5. Update carry (sum // 10).
'''
if len(arr1) == 0 and len(arr2) > 0:
return arr2
if len(arr1) > 0 and len(arr2) == 0:
return arr1
if len(arr1) == 0 and len(arr2) == 0:
return []
else:
import numpy as np
maxlen = max(len(arr1), len(arr2))
arr1dif = maxlen - len(arr1) + 1
arr2dif = maxlen - len(arr2) + 1
arr1resized = np.pad(arr1, (arr1dif, 0), 'constant')
arr2resized = np.pad(arr2, (arr2dif, 0), 'constant')
L = len(arr1resized)
arrnew = [0 for num in range(maxlen+1)]
carry = 0
elementsum = 0
for i in range(L-1, -1, -1):
elementsum = (arr1resized[i] + arr2resized[i] + carry)
arrnew[i] = elementsum % 10
#print(arrnew[i])
carry = elementsum // 10
#print(carry)
i=i-1
return arrnew
Example:
arr1 = [3,2,1,0,4,9]
arr2 = [5,1,6,4]
elementsum2array(arr1, arr2)
[0, 3, 2, 6, 2, 1, 3]
This was also done in Java here:
sum of two arrays element wise?

How to calculate numbers of "uninterrupted" repeats in an array in python?

I have a 0,1 numpy array like this:
[0,0,0,1,1,1,0,0,1,1,0,0,0,1,1,1,1,0,0,0]
I want to have a function that tells me number 1 is repeated 3,2,4 times in this array, respectively. Is there a simple numpy function for this?
This is one way to do it to find first the clusters and then get their frequency using Counter. The first part is inspired from this answer for 2d arrays. I added the second Counter part to get the desired answer.
If you find the linked original answer helpful, please visit it and upvote it.
from scipy.ndimage import measurements
from collections import Counter
arr = np.array([0,0,0,1,1,1,0,0,1,1,0,0,0,1,1,1,1,0,0,0])
cluster, freq = measurements.label(arr)
print (list(Counter(cluster).values())[1:])
# [3, 2, 4]
Assume you only have 0s and 1s:
import numpy as np
a = np.array([0,0,0,1,1,1,0,0,1,1,0,0,0,1,1,1,1,0,0,0])
# pad a with 0 at both sides for edge cases when a starts or ends with 1
d = np.diff(np.pad(a, pad_width=1, mode='constant'))
# subtract indices when value changes from 0 to 1 from indices where value changes from 1 to 0
np.flatnonzero(d == -1) - np.flatnonzero(d == 1)
# array([3, 2, 4])
A custom implementation?
def count_consecutives(predicate, iterable):
tmp = []
for e in iterable:
if predicate(e): tmp.append(e)
else:
if len(tmp) > 0: yield(len(tmp)) # > 1 if you want at least two consecutive
tmp = []
if len(tmp) > 0: yield(len(tmp)) # > 1 if you want at least two consecutive
So you can:
array = [0,0,0,1,1,1,0,0,1,1,0,0,0,1,1,1,1,0,0,0]
(count_consecutives(lambda x: x == 0, array)
#=> [3, 2, 4]
And also:
array = [0,0,0,1,2,3,0,0,3,2,1,0,0,1,11,10,10,0,0,100]
count_consecutives(lambda x: x > 1, array)
# => [2, 2, 3, 1]

Categories

Resources