How to calculate numbers of "uninterrupted" repeats in an array in python? - 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]

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.

how to subtract elements within an array using for loops in python

I have an array a = [4,3,2,1]
What I am trying to achieve is that I need a single value on subtracting the elements in the array that is 4-3-2-1.
I tried the below this using for loop but it does not seem to work. I don't seem to get the right value on execution.
def sub_num(arr):
difference = arr[0]
n = len(arr)
print(n)
print(i)
for i in n: difference = arr[n] - arr[n-1]
return(difference)
If you have a list a:
a = [4, 3, 2, 1]
And wish to get the result of 4 - 3 - 2 - 1, you can use functools.reduce.
>>> from functools import reduce
>>> a = [4, 3, 2, 1]
>>> reduce(int.__sub__, a)
-2
>>>
You can solve with nested lists:
b = sum([a[0],*[-x for x in a[1:]]])
Simpler solution without for:
Since 4-3-2-1 is equal to 4-(3+2+1):
a[0]-sum(a[1:])
You could modify your code like this
def sub_num(arr):
difference = arr[0]
n = len(arr)
print(n)
for i in range(1,n):
difference = difference - arr[i]
return difference
Note:
Printing value of i without defining it is not possible

Compare and multiply elements in a list

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

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?

Function to Create a Zigzag Array in Python

I'm trying to create an array of positive and negative integers, representing distances north and south of a location - I need to see the elements of the array in zigzag order.
This means that the largest member appears first, the smallest member appears second, and the remaining elements alternate between the larger members decreasing from the largest and the smaller members increasing from the smallest.
I.e. the array [1, 3, 6, 9, -3] becomes [9, -3, 6, 1, 3].
I'm trying to complete the function wiggleArrangeArray, which takes one argument, an integer array of n integers.
Required Input Format, Constraints, and Output Format
I'm not sure how to say
"if the item in the array is larger than the other items in the array, display it first."
"if the item is smaller than the other items in the array, display it second."
"then alternate between the next largest numbers, and the next smallest numbers"
def wiggleArrangeArray(intArr):
for i in intArr
#if i > other items in intArr
#display that i at index value 0
#if i < other items in intArr
#display that i at index value 1
#if i < i at index value 0 and > other items in intArr, display it next
#if i > i at index value 1 and < other items in intArr, display it next
#repeat last two lines for continuing values
Please help if possible. Here's a link to the solution in C++ but I need it in Python. Thanks.
Edit: The function needs to work with the following tests:
f = open(os.environ["OUTPUT_PATH"], "w")
_intArr_cnt = int(raw_input())
_intArr_i=0
_intARR = []
while _intArr_i < _intArr_cnt:
_intArr_item = int(raw_input());
_intArr.append(_intArr_item)
_intArr_i+=1
res = wiggleArrangeArray(_intArr);
for res_cur in res:
f.write( str(res_cur) + "\n" )
f.close()
The altered C++ code
Note the algorithm you provided does calculate some sort of zigzag, but it is not the zigzag you are looking for. For future reference I will leave it here.
In the C++ code you provided, they only look for a sequences that satisfies a < b > c < d > e < f, you are looking however for a sequence with the 1-max, 1-min, 2-max, 2-min,...
Your link provides you a solution which you can copy nearly verbatim in Python. You only define a swap function:
def swap(arr,i,j):
t = arr[i]
arr[i] = arr[j]
arr[j] = t
Next you can simply modify the code:
def zigZag(arr):
n = len(arr)
# Flag true indicates relation "<" is expected,
# else ">" is expected. The first expected relation
# is "<"
flag = True
i = 0
while i<= n-2:
if (flag): # "<" relation expected
# If we have a situation like A > B > C,
# we get A > B < C by swapping B and C
if arr[i] > arr[i+1]:
swap(arr,i,i+1)
else: # ">" relation expected
# If we have a situation like A < B < C,
# we get A < C > B by swapping B and C
if arr[i] < arr[i+1]:
swap(arr,i,i+1)
flag = not flag # flip flag
i += 1
Mind that this is rather un-Pythonic, so you can simply improve it like:
def swap(arr,i,j):
arr[i],arr[j] = arr[j],arr[i]
def zigZag(arr):
n = len(arr)
for i in range(len(arr)-1):
if not i&1:
if arr[i] > arr[i+1]:
swap(arr,i,i+1)
elif arr[i] < arr[i+1]:
swap(arr,i,i+1)
return arr
Here tuple assignment is used to swap elements in the list, a range is used to iterate over the indices, we can furthermore use an elif instead of an if in an else, and for I've abandoned the flag by using a modulo check.
Your zigzag function
You can simply solve the problem by sorting the list, and using two pointers that each time emit the most left, the most right and walk towards each other. In other words something like:
def zigZag(arr):
srt = sorted(arr)
left = 0
right = len(srt)-1
result = []
while left < right:
result.append(srt[right])
right -= 1
if left < right:
result.append(srt[left])
left += 1
return result
Here Python implementation example.
Sort your data in reversed order (9, 6, 3, 1, -3)
Split by two lists (9, 6) (3, 1, -3)
Iterate over both (using zip_longest) first one from start, second in reverse order (9, -3), (6, 1) (None, 3)
Unpack tuples created by zip_longest (chain(*zip...)) so you get iterable data (9, -3, 6, 1, None, 3)
Remove None value in case if the list has an odd number of elements (if x is not None)
Example:
from itertools import zip_longest, chain
def zig_zag_array(data):
data = sorted(data)
mid = len(data) // 2
return [
x for x in chain(*zip_longest(data[mid:][::-1], data[:mid])) if x is not None
]
Test:
if __name__ == "__main__":
data = [1, 3, 6, 9, -3]
expected = [9, -3, 6, 1, 3]
output = zig_zag_array(data)
assert output == expected, f"Odd length: {output=}"
data = [1, 3, 6, 9, -3, 0]
expected = [9, -3, 6, 0, 3, 1]
output = zig_zag_array(data)
assert output == expected, f"Even length: {output=}"
print("PASSED")

Categories

Resources