Python nested loop miscounting instances of integers in list - python

I'm banging my head against the wall trying to figure out why this nested loop is miscounting the number of times an integer occurs in a list. I've set up a function to take two lines of input, n and ar, where n is the number of integers in ar and ar is the list of integers. My code is below:
import sys
n = sys.stdin.readline()
n = int(n)
ar = sys.stdin.readline()
ar = ar.split(' ')
ar = [int(i) for i in ar]
def find_mode(n,ar):
# create an empty dict and initially set count of all integers to 1
d = {}
for i in range(n):
d[ar[i]] = 1
for i in range(n):
# hold integer i constant and check subsequent integers against it
# increase count if match
x = ar[i]
for k in range(i+1,n):
if ar[k] == x:
d[ar[k]] += 1
print(d)
The counter seems to be increasing the count by 1 every time, which leads me to believe it's a problem with the nested loop.
>>> 9
>>> 1 2 3 4 4 9 9 0 0
{0: 2, 1: 1, 2: 1, 3: 1, 4: 2, 9: 2}
OK
>>> 10
>>> 1 2 3 4 4 9 9 0 0 0
{0: 4, 1: 1, 2: 1, 3: 1, 4: 2, 9: 2}
Count of 0 increased by +2
>>> 11
>>> 1 2 3 4 4 9 9 0 0 0 0
{0: 7, 1: 1, 2: 1, 3: 1, 4: 2, 9: 2}
Count of 0 increased by +3
I understand there might be more efficient or "pythonic" ways to count the amount of times a number occurs in a list but this was the solution I came up with and as someone still learning Python, it would help to understand why this exact solution is failing. Many thanks in advance.

This is because for each distinct number in the list (call it x) you count the number of subsequent appearances. This is fine if a number only occurs twice but if it occurs multiple times you will over-count for each additional appearance.
For example: [0, 0, 0, 0]. You iterate over the list and then for each item you iterate over the list that follows that item. So for the first 0 you count a total of 3 subsequent 0s. For the second however you will count a total of 2 and for the third a total of 1 which makes 6. This is why you have 3 too much in the end.
You can achieve this task by using collections.Counter:
>>> from collections import Counter
>>> d = Counter(ar)

I'm not exactly sure that I can fix your specific problem, but would something like this work instead?
d={}
for x in ar:
d[x] = d.get(x, 0) + 1
I understand that you want to fix your existing work as a learning exercise, but I'm not sure that that approach is even the right one. As it is, I can't really tell what you're going for, so it's hard for me to offer specific advice. I would recommend that you don't throw good time after bad.

python has a method to do exactly what you're describing.
It's called .count().
If you do ar.count(3), it will return the number of occurences of 3 in the list ar.
** In your case:**
There's no need for a nested loop as you only need one loop.
Try this:
dic = {}
for num in ar:
if num not in dic:
dic[num] = 1
else:
dic[num] += 1
This would produce the dict you want with the numbers and their occurences

You can refer to other answers as to how you should solve this problem more efficiently, but to answer the question you're asking (Why doesn't this nested loop work?):
To visualize what your nested loop is doing consider the following input:
0 0 0 0 0
Your algorithm will count the following:
0 0 0 0 0
^ ^ ^ ^ ^ (5)
then,
0 0 0 0 0
^ ^ ^ ^ (4)
then,
0 0 0 0 0
^ ^ ^ (3)
then,
0 0 0 0 0
^ ^ (2)
and finally,
0 0 0 0 0
^ (1)
What happens is it counts the number of 0's multiple times over. In this instance it will count
15 0's (5+4+3+2+1)

itertools are your friend
from itertools import groupby
def group_by_kv_l(n):
l = []
for k, v in groupby(n):
l.append((len(list(v)),int(k)))
return l
def group_by_kv_d(n):
d = {}
for k, v in groupby(n):
d[(int(k))] = len(list(v))
return d
if __name__ == "__main__":
n = input().split()
n = "".join(n)
print(
"STDIN: {}".format(n)
)
print(
group_by_kv_l(n)
)
print(
group_by_kv_d(n)
)

Related

Python Nested Loop explanation

I’m new to python coding and i dont understand why the nested for loop is only returning 0 1 2 for the firs Iteration.
Input:
x = 3
for i in range (x):
for j in range (x):
x = 2
print (i, '',j)
Output:
0 0
0 1
0 2
1 0
1 1
2 0
2 1
x is changed after it's passed to range to make range(3). You only see the effects of the change (i.e. range(2)) on the next loop.
The Concept behind Nested for Loops:
Let us break this problem down (I am a beginner myself!)
x = 3
for i in range (x):
Now the range function has 3 parts (start, stop, step)
start: start from this number
stop: maximum value
step: increments by this value
when we say range(x); it assumes x=3 as the stop/max value of range. The start value is by default taken as 0, and the step value is by default taken as 1. So the range we get here is [0,1,2,3) {starts from 0 and stops at 3}
So the values that 'i' can take are 0,1,and 2 because 3 is max of the range; it is not included in the values i and j can take.
So output until this point:
for i in range (x):
for j in range (x):
(printing i and j separated by a whitespace)is:
0 0
0 1
0 2
0 is printed at the start and the loop is iterated 2 more times.
(you are getting all zeroes printed first for i as the statement you have written sends an instruction to print all the values of j for one value in the outer/main for loop; because loop for j is nested inside the loop for i)
Now, x= 2 means that from this point, values 'j' can take are 0,1. Hence the second part of the output:
1 0
1 1
Similarly, the the third part of the output is:
2 0
2 1
Hence the final output you get is:
0 0
0 1
0 2
1 0
1 1
2 0
2 1
Long version
In the for statement "for target in expression :" the second part is an iterable object.
range is not a 'reserved' word; it is the name of a built-in type(class) witch is iterable
Note : The syntax highlighter cheats if it highlights range as a reserved word. It does this, because this is generally useful as range is mainly used in association with for. But this does not change what range is. However, this can mislead you.
As a result of the above:
the correct typing is range(x) not range (x)
range(x) build an objet of type range and initialize it with x.
Short answer
x is interpreted when it is passed to range().
Code to print the range objects:
x = 3
range_i = range(x)
print(f"i loop x: {x}, range(x):{range_i}")
for i in range_i:
range_j = range(x)
print(f"j loop x: {x}, range(x):{range_j}")
for j in range_j:
x = 2
print(i, ' ',j)
Output
i loop x: 3, range(x):range(0, 3)
j loop x: 3, range(x):range(0, 3)
0 0
0 1
0 2
j loop x: 2, range(x):range(0, 2)
1 0
1 1
j loop x: 2, range(x):range(0, 2)
2 0
2 1
Rule of thumb
Unless you really know what you are doing, do not mess with the expression of the for statement.
lst = ['a', 'b', 'c', 'd']
for x in lst:
if x == 'b':
lst.remove('a')
print(x, end = ' ')
gives
a b d
And
lst = ['a', 'b', 'c', 'd']
for x in lst:
if x == 'd':
lst.insert(0, 'z')
print(x, end = ' ')
does not end and 'z' never appears:
a b c d d d d d d d d d d d d d ...
Musing around
Note : what follows is NOT recommended
You can redefine range. (The example below is a very simplified redefinition: it does not take in account the second version of range : range(start, stop[, step]) neither it cares for other range specifications)
def range(n):
i = 0
while i <= n: # note the use of '<=' instead of '<'
yield i
i += 1
And now 'range' does not behave as it should.
Example:
for i in range(3):
print(x, end = ' ')
gives:
0 1 2 3
Yes: 0..3 not 0..2 as the 'true' range.

Python: nested for loop

Hello am a beginner to python and I have been stuck at this problem for awhile now. I want to start with 2 lists:
list1 = [1, 2, 3, 4, 5, 6, 7, 8, 9]
list2 = [a, b, c]
And write a function that will give me this output:
a
1
2
3
b
4
5
6
c
7
8
9
I have tried using nested for loops and a counter but I am unsure how to obtain the above result.
counter = 0
for i in list2:
print(i)
for j in list1:
print(j)
counter += 1
if counter == 3:
counter = 0
break
Any help would be appreciated!
Here's one possible way, closer to what you intended to write:
j = 0
for i in list2:
print(i)
for _ in range(3):
print(list1[j])
j += 1
Here's an option:
for i, v1 in enumerate(list2):
print(v1)
for v2 in list1[i*3:(i+1)*3]:
print(v2)
You don't need to make and update your own counters here. The built-in enumerate() function generates a counter for you and automatically updates it for each step of the loop. Then you can use list slicing to get the right three values of the inner list.
Try this:
for i in list2:
print(i)
# here you don't have to take it back to 0
counter = 0
for j in range(len(list1)):
# Use range to loop the list so you can cut it off later
# from the position you reach the third element
print(list1[j])
counter += 1
if counter == 3:
list1 = list1[j + 1:]
break
It outputs what you expect:
a
1
2
3
b
4
5
6
c
7
8
9

questions about the nested loop on array [duplicate]

This question already has answers here:
List of lists changes reflected across sublists unexpectedly
(17 answers)
Closed 5 years ago.
s = [[0]*3]*3
i = 0
while i < 3:
j = 0
while j < 3:
print(s[i][j])
s[i][j] += 1
j += 1
i += 1
The printed result of the above code really confused me, Why the second and third column of the array become [1,1,1] and [2,2,2] but not [0,0,0]?
Because when you create a list of lists using [[0]*3]*3, you're creating one list [0,0,0] three times, so you'll have [[0,0,0],[0,0,0],[0,0,0]], but all of the sub lists ([0,0,0]) are really refrencing the same list, since you just created one and multiplied it by 3 to create the others, so changing one list will make changes to the other lists.
To prevent this, create independent zero-ed lists using a list comprehension:
s = [[0]*3 for i in range(3)]
i = 0
while i < 3:
j = 0
while j < 3:
print(s[i][j])
s[i][j] += 1
j += 1
i += 1
print(s) # just to see the final result
Which outputs:
0
0
0
0
0
0
0
0
0
[[1, 1, 1], [1, 1, 1], [1, 1, 1]]
because the way you create array is
s = [[0]*3]*3
which means all elements of s is the same list and once you change one of them, the rest of them will be changed too
so if you want to get [[0,0,0],[1,1,1],[2,2,2]] as you said,try this:
import numpy as np
s = np.zeros((3,3)) #in this way you can get a 3*3 array
i = 0
while i < 3:
j = 0
while j < 3
s[i][j] += i #not plus 1,otherwise you will get [[1,1,1],[1,1,1],[1,1,1]]
j += 1
i += 1

Can't delete not last single object [duplicate]

This question already has answers here:
How to remove items from a list while iterating?
(25 answers)
Closed 6 years ago.
My code should recieve a list of numbers and then output on the screen the only numbers which repeat more then once. I don't know why but it don't work with the numbers in the middle of list. My code:
a = [int(i) for i in (input().split())]
a.sort()
for number in a:
if a.count(number)==1:
a.remove(number)
else:
a.remove(a.count(number)-a.count(number)+number)
for number in a:
print(number, end=' ')
I tried changing if on while on 4th string, but then the last number is left in the list.
It should work like:
Sample Input 1: 4 8 0 3 4 2 0 3 Sample Output 1: 0 3 4
Sample Input 2: 10 Sample Output 2:
Sample Input 3: 1 1 2 2 3 3 Sample Output 3: 1 2 3
Sample Input 4: 1 1 1 1 1 2 2 2 Sample Output 4: 1 2
You could approach this problem using set instead:
a = list(map(int, input().split()))
print(" ".join(map(str, set(i for i in a if a.count(i) > 1))))
Explanation:
Firstly, it looks like you should read up on the map function. Instead of a = [int(i) for i in (input().split())], you can just use list(map(int, input().split())), which is much more Pythonic.
Secondly, the important part of the second line is set(i for i in a if a.count(i) > 1), which just creates a new list containing only the duplicates from a (i.e. [1, 2, 3, 2, 3] becomes [2, 3, 2, 3] and then applies set to it, which converts [2, 3, 2, 3] into {2, 3} (which is a set object).
In case you're wondering what map(str, ...) is for, it's so that you can print each of the elements inside your new set (e.g. {2, 3}).
You can use built-in lib collections to count list items and filter it by a required condition.
import collections
ints = map(int, input().split())
count = collections.Counter(ints)
print filter(lambda x: count[x] > 1, count)

A loop for adding list element without high memory performance?

Everybody,
a =[0, 0, 2, 4, 6]
x=5
This a list (a) and a fix value (x).
I need a loop codes which must add with x every element of list and add this value with previous list elements in every loop ( loop must continue as x value). Other words result should like below:
0
0
2
4
6
0
0
7
9
11
0
0
12
14
16
0
0
15
19
21
0
0
21
24
26
I prepared codes as below but it doesn’t work. Other words produce something as below (incorrect)
i=0
counter=0
while counter < x:
for i in a:
if i >0:
i=i+x
elif i ==0:
i=0
print i
counter=counter+1
0
0
7
9
11
0
0
7
9
11
0
0
7
9
11
0
0
7
9
11
0
0
7
9
11
So, I need to help for this…
Thank you.
I think this does mostly what you want (at least, as I understand the question)...
def make_it_so(a, x):
i = 0
counter=0
while counter < x:
for i in a:
if i == 0:
yield 0
else:
yield i + counter * x
counter = counter + 1
# Demo
for item in make_it_so([0, 0, 2, 4, 6], 5):
print item
Note that I've made it a generator function. You could easily turn it into a regular function that returns a list if you created an output list at the top of the function and swapped yield ... for output_list.append(...) and then return output_list at the end of the function...
The key here is to understand that in the first loop, you are adding 0 to all of the (non-zero) items. In the second loop, you are adding x. In the third loop, you're adding the x + x (since the first loop added x and now you're adding x more). In general, for the Nth loop, you'll be adding (N-1) * x to all of the non-zero items. So, you just need to keep track of N, (or N-1). In fact, your original code was already doing this (with counter), so we just re-purpose that and it's all good.
You need to change the values in a, not just add to the numbers you get out of a (because you'll keep getting the same ones out). Also, you need to print out the original values.
def process(x, memo):
return [n+x if n else n for n in memo]
res = a
memo = a
for _ in range(x - 1):
memo = process(x, memo)
res.extend(memo)

Categories