Counting consecutive numbers in all columns of a 2D array - python

I have a 2d array, X, that looks like this
[0, 0, 0, 2, 1]
[1, 2, 1, 0, 1]
[2, 2, 1, 0, 0]
[0, 0, 1, 2, 0]
I'm trying to iterate through the entire 2D array to try and count all the instances where there are 2 consecutive elements in a column. E.g. X above would return 4 (X[1][1] == X[2][1] && X[1][2] == X[2][2] && X[2][2] == X[3][2] and so on)
I'm finding this very hard to visualize. So far I have:
def get_opposite(number):
if number == 2: return 1
if number == 1: return 2
def counter(X, number):
count = 0
for i in range(len(X)):
for j in range(len(X[i])-1):
if X[i][j] == X[i][j+1] and X[i][j] != 0 and X[i][j] != get_opposite(number):
count += 1
return count
I keep either getting vastly incorrect results, or IndexError, it should be fairly straight forward but I'm not sure what I'm doing wrong

If you compare the example you give in the text with your actual code, you'll notice your code is comparing with the value on the right, not the with the value below it. You need to apply +1 to the first index, not the second. This also means the range of your loops has to be adapted accordingly.
Secondly, you don't need the first function. The equality comparison is enough.
Also, I removed the second argument of the function, as it serves no role:
def counter(X):
count = 0
for i in range(len(X)-1):
for j in range(len(X[i])):
if X[i][j] == X[i+1][j] and X[i][j] != 0:
count += 1
return count

Related

How can I move zeroes to the end of a list?

I have an array of integers like:
nums = [0, 1, 0, 3, 12]
I want to move all '0's to the end of it, while maintaining the relative order of the non-zero elements. So the desired output is [1, 3, 12, 0, 0].
I made the following attempts:
temp = 0
for i in range(len(nums)):
if nums[i] == 0:
nums.pop(i)
temp += 1
print(temp)
In this code, I got an error saying that nums[i] has an index out of range. Why? len(nums) == 5, so the i values should all be valid.
nums = [0,1,0,3,12]
nums.sort()
temp = 0
for i in range(len(nums)-1):
if nums[i] == 0:
print(nums[i])
temp +=1
nums.pop(i)
print(nums)
for _ in range(temp):
nums.append(0)
print(nums)
With this code, the first print gives an output of [0, 1, 3, 12], so in the final result, not all of the zeroes are moved. Why were they not all popped by the first loop?
I think one way you can get the desired output is to separate the nums list into two list that doesn't contain zeros and one does using list comprehension and concat the two list together
nums = [0,1,0,3,12]
new_list = [n for n in nums if n != 0] + [n for n in nums if n == 0]
Edit: per #DanielHao's suggestion, you can also use sorted key lambda with lambda x: not x which will then interprets zeroes as 1 and non-zeroes as 0 when sorting the list
nums = [0,1,0,3,12]
nums[:] = sorted(nums, key=lambda x: not x)
nums = [0, 1, 0, 3, 12]
temp =0
new_nums=[]
for x in range(0,len(nums)):
if(nums[x]==0):
temp +=1
else:
new_nums.append(nums[x])
while(temp != 0):
new_nums.append(0)
temp -=1
print(new_nums)
This code work efficiently to produce desired output.
Q1) When you pop out the item, the length of nums is decreased. For loops will not adjust the range being iterated over, so in this case range(len(nums)) will always be range(5) as this was the original length. This means that nums[4] is called, which results in an index error because the item that used to be at this index has moved backwards in the list due to the removal of prior values.
A solution is to use a while loop, as the condition i < len(nums) will be checked at every iteration. You will have to manually increment i.
Q2) If the indices of values in the list decreases as items are popped, some values will be skipped.
A solution in tandem with a while loop is to only increment i if the condition nums[i] == 0 is not met.
nums = [0,1,0,3,12]
nums.sort()
temp = 0
i = 0
while i < len(nums):
if nums[i] == 0:
print(nums[i])
temp += 1
nums.pop(i)
else:
i += 1
print(nums)
for _ in range(temp):
nums.append(0)
print(nums)
There are definitely easier ways to solve the same problem, as shown in other solutions, but I hope this way is easy to understand.
1] You are popping the list element so the list length changes hence getting out of index error
2] Code:
nums = [0,1,0,3,12]
# Get count of 0's
count = nums.count(0)
# List without 0's
nums = [ i for i in nums if i!=0 ]
# Add 0's to the end
[nums.append(0) for i in range(count)]
print(nums)

Comparing elements in a list in order using loop or stack/queue

I have a list ls with integer elements between 0 and 100. I want to build a function that counts a number of elements until it encounters an element that has a larger value, and appends the count to the solution list.
In other words, if ls = [5, 10, 1, 1, 20, 1], the solution should be [1, 3, 2]:
1 comes from the first element, 5
3 comes from the second to fourth elements, 10, 1, 1
2 comes from the last two elements, 20, 1
(If ls = [7, 3, 9], the return should be [2,1].)
I used a for loop to perform the task:
def compare_and_count(ls):
answer = []
num = 1
ref = 0
for j in range(1, len(ls)):
try:
if ls[ref + j] <= ls[ref]:
num += 1
else:
answer.append(num)
num = 1
ref = ref + j
except IndexError:
break
answer.append(num)
return answer
I tried to do a value comparison with two movable references, but this raises IndexError and also sometimes neglects the final counting and hence returns an incorrect list. (I added try...except to be free of the IndexError, but the latter problem is still unsolved)
I assume either loop or stack/queue is the most concise way to solve this, but I couldn't land an optimal way yet. Any insight is appreciated.
Edited the answer to correct it .
def my(a):
count = 1
maxval = a[0]
answer = []
for i in range(1, len(a)):
if a[i] > maxval:
maxval = a[i]
answer.append(count)
count = 1
else:
count += 1
answer.append(count)
return answer
I tried to do a value comparison with two movable references
You dont need to, just one moving index i and one count counter with a maxval to check the condition.

Find occurrences of neighboring elements

Want to write a simple program that shoud count the number of occurrences of "11" in an input list of zeros and ones. The output should return a single number, which is the number of occurrences of "11".
I have implemented the following code:
def count_ones(seq):
# return the number of occurrences as a number
return sum(1 for i in seq if i != 0)
print(count_ones([0, 0, 1, 1, 1, 0])) # this should print 2
Now it doesn't work as needed. How to improve the given code to get a proper output?
Using zip
def count_ones(seq):
# find pairs where current == next element == 1
return sum(1 for x, y in zip(seq, seq[1:]) if x == y == 1)
Tests
print(count_ones([1, 0, 1]) # Output: 0
print(count_ones([1, 0, 1, 1]) # Output: 1
print(count_ones([0, 0, 1, 1, 1, 0])) # Output: 2

Trying to spread out difference between cells in list

I have a list that moves around 100 by a small bit (see the list in the code below). For the days that I have 0, I would like to spread out the difference of the previous cells.
(I'm counting zero as the first cell in the text below, because python)
To give an example, the original_list below has the value 100.41191500000001 in cell 2, and 0 in cells 3,4,5. I would like to take the change abs(original_list[1]-original_list[2])=1,26108000000001, and spread it out over the three days. So basically I would like cell 2 to be occupied by original_list[1]+(1.26108000000001/4), cell 3 to be new_list[2] + (1.26108000000001/4), cell 4 to be new_list[3] + (1.26108000000001/4), and finally cell 5 to also be new_list[4] + (1.26108000000001/4).
I divide by four because I want the change that happened between cell 1 and 2, to be spread out evenly between cells 1 to 5.
The code below presents what I've tried, but I'm not sure if this is the best approach. Any help would be greatly appreciated.
original_list = [98.87464, 99.150835, 100.41191500000001, 0, 0, 0, 101.650165, 0, 0, 0, 0, 0, 101.850421, 0, 99.970131, 100.244205, 98.550495, 0, 0, 97.496535, 97.971645]
new_list = [0 for i in range(len(original_list))]
for i in range(len(original_list)):
j = i
if original_list[i] == 0:
while original_list[j] == 0:
j += 1
elif original_list[i] != 0:
new_list[i] = original_list[i]
if j != i:
for k in range(0,j-i+1):
new_list[i+k] = new_list[i-1] + abs(new_list[i]-new_list[i-1])/(j-i+1)
print(new_list)
It's not the best approach as it will not give your expected result.
The for loops iterates over all the elements of the range. You have an inner for loop in the if j != i: block which calculate the replacement values for the following 0's. When the control is returned to the main for loop, it is going to recalculate those values, giving unexpected numbers.
I think your math to calculate new_list[i+k] is wrong. If I understood correctly, it does not reproduce what you describe in your question.
In your case a while loop is recommended. In the case where original_list[i] != 0: you increment the index by 1, in the case where original_list[i] == 0: you need to set the index of the proper value so that it corresponds to the next non null element in your original list, which should be i = j.
And of course you need to fix the math.
I would wrote the code in this way:
new_list = []
i = 0
while i < len(original_list):
if original_list[i] == 0:
j = i+1
while original_list[j] == 0 and j < len(original_list):
j += 1 #searching the index of the next non null value
#please check if the following math is what you really need, fix it in case.
diff = original_list[j] - new_list[-1]
inslist = [new_list[-1] + (k*diff)/(j-i+1) for k in range(1, (j-i+1))]
new_list.extend(inslist)
i = j
elif original_list[i] != 0:
new_list.append(original_list[i])
i += 1
You can compare the two list with: print([(o, n) for o, n in zip(original_list, new_list)]) and see if each original - new pair of values is correct.

Reducing a N-Sum to a Two Sum

I've recently came across a cool algorithm to reduce any problem of the sort "Find n numbers in an array that sum to a target" to a Two Sum problem. However, I am having a hard time understanding one line of the code.
def findNsum(nums, target, N, result, results):
if len(nums) < N or N < 2 or target < nums[0]*N or target > nums[-1]*N: # early termination
return
if N == 2: # two pointers solve sorted 2-sum problem
l,r = 0,len(nums)-1
while l < r:
s = nums[l] + nums[r]
if s == target:
results.append(result + [nums[l], nums[r]])
l += 1
while l < r and nums[l] == nums[l-1]:
l += 1
elif s < target:
l += 1
else:
r -= 1
else: # recursively reduce N
for i in range(len(nums)-N+1):
if i == 0 or (i > 0 and nums[i-1] != nums[i]):
findNsum(nums[i+1:], target-nums[i], N-1, result+[nums[i]], results)
results = []
findNsum(sorted(nums), 0, 3, [], results)
return results
The condition:
if i == 0 or (i > 0 and nums[i-1] != nums[i]):
Does not make sense to me. Why do I have to check if nums[i-1] != nums[i]? If I try it out with, say, with nums = [-1, 0, 1, 2, 2, -1, -4], I get [[-4, 2, 2], [-1, -1, 2], [-1, 0, 1]] with the condition. If I take it out I get [[-4, 2, 2], [-1, -1, 2], [-1, 0, 1], [-1, 0, 1]]. Can any one make sense of this?
Cheers!
The condition nums[i-1] != nums[i] is to avoid creating duplicate solutions when picking the first element, which can see in your output in the second example. This problem wants to find all unique solutions, not all possible solutions, hence we want to drop the second [-1,0,1]

Categories

Resources