This question already has answers here:
How do I iterate through two lists in parallel?
(8 answers)
Closed 6 years ago.
First of all below are the list not tuple also would like to append the data in both the list and above all that need to know the reason of error along with solution
Trying to compare two list basically, here "ref" is the reference list and "obt" is product list, while running code(below) an error is generated.
What is the solution for it?
Also would like to apply the same to the data frames in pandas, what should be the code for it?
ref =[1,2,3,4]
obt =[0.5,1,1.5,5]
i,j=0
for i,j in obt,ref:
global i,j
if (obt[i] <= ref[j]):
print ("all ok")
else:
print("error")
i=i+1
j=j+1
I think you would be good with zip:
for o, r in zip(obt, ref):
if o <= r:
print ("all ok")
else:
print("error")
Here is what zip(obt, ref) will produce:
[(0.5, 1), (1, 2), (1.5, 3), (5, 4)]
And, while looping over it you can compare the values from the tuple.
This assignment statement should not work:
i,j=0
Try this instead:
>>> i, j = 0, 0
>>> i
0
>>> j
0
But if you wanted to compare two different lists here's how you do it:
import itertools
for i, j in itertools.zip_longest(list1, list2):
# do stuff with i and j
# if either i or j is none then
# they are not the same lengths
You could also use a try, except statement if you are running into issues with lists of different length
Like:
try:
for i, j in obt, ref:
# Compare lists
except: # Enter error after except
# Do something else
You will need to use other variables then i and j. These will be altered by the for loop. It is better to use for x in range(len(list) for the indexing
Python for loops are actually foreach loops. You could do
ref =[1,2,3,4]
obt =[0.5,1,1.5,5]
for i in range(len(ref)):
if (obt[i] <= ref[i]):
print ("all ok")
else:
print("error")
The range function will iterate through the numbers 0 to the length of your array, range(len(ref)) = [0,1,2,3], you can then use for which in Python is actually a foreach to grab these numbers.
The enumerate function will give you both the index and the value, so you can also do
ref =[1,2,3,4]
obt =[0.5,1,1.5,5]
for i,r in enumerate(ref):
if (obt[i] <= r):
print ("all ok")
else:
print("error")
The enumerate function gives back the index and ref[i], which is being stored in i and r. This example better shows how Python is behaving like a foreach loop and not a for loop.
The reason for the error
On the line i,j=0, you have to give j a value as well, so you would do i,j=0,0. Now what happens here is that 0,0 is that same thing as (0,0). It's a tuple. The tuple then has to be unpacked. When a tuple is unpacked the first variable, i, gets the first value 0. The second variable, j, gets the second value. And if there where more values in the tuple this would continue until every value in the tuple is unpacked to some variable.
Once this is fixed we can proceed to the next line. for i,j in obt,ref. Once again, you will see the exact same error. This time python is expecting to iterate over something in the form of [(2 value tuple), (2 value tuple), (2 value tuple), (2 value tuple)].
The zip function can be used to create this structure, as has been suggested above, or if you prefer using the indexes in a for loop, you could use for i in range(len(ref)) to iterate over the list of indexes [0,1,2,3]. This
only work if ref and obt are the same length, otherwise you have to use zip.
Both ways work, using zip, or the method I mentioned, it's up to you how you want to solve the problem. If one list is longer than the other, zip will truncate the longer lists. The itertools.zip_longest solution that Games Braniac mentioned will extend the shortest length list so that they are the same size, and then zips them up.
Related
So I was trying to complete this kata on code wars and I ran across an interesting solution. The kata states:
"Given an array of integers, find the one that appears an odd number of times.
There will always be only one integer that appears an odd number of times."
and one of the solutions for it was:
def find_it(seq):
return [x for x in seq if seq.count(x) % 2][0]
My question is why is there [0] at the end of the statement. I tried playing around with it and putting [1] instead and when testing, it passed some tests but not others with no obvious pattern.
Any explanation will be greatly appreciated.
The first brackets are a list comprehension, the second is indexing the resulting list. It's equivalent to:
def find_it(seq):
thelist = [x for x in seq if seq.count(x) % 2]
return thelist[0]
The code is actually pretty inefficient, because it builds the whole list just to get the first value that passed the test. It could be implemented much more efficiently with next + a generator expression (like a listcomp, but lazy, with the values produced exactly once, and only on demand):
def find_it(seq):
return next(x for x in seq if seq.count(x) % 2)
which would behave the same, with the only difference being that the exception raised if no values passed the test would be IndexError in the original code, and StopIteration in the new code, and it would operate more efficiently by stopping the search the instant a value passed the test.
Really, you should just give up on using the .count method and count all the elements in a single pass, which is truly O(n) (count solutions can't be, because count itself is O(n) and must be called a number of times roughly proportionate to the input size; even if you dedupe it, in the worst case scenario all elements appear twice and you have to call count n / 2 times):
from collections import Counter
def find_it(it):
# Counter(it) counts all items of any iterable, not just sequence,
# in a single pass, and since 3.6, it's insertion order preserving,
# so you can just iterate the items of the result and find the first
# hit cheaply
return next(x for x, cnt in Counter(it).items() if cnt % 2)
That list comprehension yields a sequence of values that occur an odd number of times. The first value of that sequence will occur an odd number of times. Therefore, getting the first value of that sequence (via [0]) gets you a value that occurs an odd number of times.
Happy coding!
That code [x for x in seq if seq.count(x) % 2] return the list which has 1 value appears in input list an odd numbers of times.
So, to make the output as number, not as list, he indicates 0th index, so it returns 0th index of list with one value.
There is a nice another answer here by ShadowRanger, so I won't duplicate it providing partially only another phrasing of the same.
The expression [some_content][0] is not a double list. It is a way to get elements out of the list by using indexing. So the second "list" is a syntax for choosing an element of a list by its index (i.e. the position number in the list which begins in Python with zero and not as sometimes intuitively expected with one. So [0] addresses the first element in the list to the left of [0].
['this', 'is', 'a', 'list'][0] <-- this an index of 'this' in the list
print( ['this', 'is', 'a', 'list'][0] )
will print
this
to the stdout.
The intention of the function you are showing in your question is to return a single value and not a list.
So to get the single value out of the list which is built by the list comprehension the index [0] is used. The index guarantees that the return value result is taken out of the list [result] using [result][0] as
[result][0] == result.
The same function could be also written using a loop as follows:
def find_it(seq):
for x in seq:
if seq.count(x) % 2 != 0:
return x
but using a list comprehension instead of a loop makes it in Python mostly more effective considering speed. That is the reason why it sometimes makes sense to use a list comprehension and then unpack the found value(s) out of the list. It will be in most cases faster than an equivalent loop, but ... not in this special case where it will slow things down as mentioned already by ShadowRanger.
It seems that your tested sequences not always have only one single value which occurs an odd number of times. This will explain why you experience that sometimes the index [1] works where it shouldn't because it was stated that the tested seq will contain one and only one such value.
What you experienced looking at the function in your question is a failed attempt to make it more effective by using a list comprehension instead of a loop. The actual improvement can be achieved but by using a generator expression and another way of counting as shown in the answer by ShadowRanger:
from collections import Counter
def find_it(it):
return next(x for x, cnt in Counter(it).items() if cnt % 2)
Before Anything this is my first time asking a question here so if there's Something wrong with the format of the question I'm sorry about that.
So basically This is a code that is supposed to get a list of numbers, and a number from user and check to see if the second input is equal to any of the elements of the list, then put those element index numbers in a new list and print it.
The thing is the error is saying 'a' is not defined but if I define it before the for loop like this:
a=[]
or
a=list()
I get this:
a[k]= c
IndexError: list assignment index out of range
Here's the code:
x = eval(input('list'))
y = eval(input('number'))
k=0
c=0
for i in x:
if y==i:
a[k]= c
k+=1
c+=1
print(a)
A list in python doesn't support index assignment. - Therefore the indices will always be numeric depending on the position 0, 1, n.
To add use append, to have a hash map use a dict.
Instead of
a[k]= c
You can use
a.append(c)
This question already has answers here:
Understanding slicing
(38 answers)
Closed 7 years ago.
The ff is a code to find the longest increasing subsequence of an array/list in this case it is d. The 4th line confuses me like what does l[j][-1] mean. Let's say i=1. What would l[j][-1] be?
d = [10,300,40,43,2,69]
l = []
for i in range(len(d)):
l.append(max([l[j] for j in range(i) if l[j][-1] < d[i]] or [[]], key=len) + [d[i]])
When I hit something like this, I consider that either I'm facing a smart algorithm that's beyond my trivial understanding, or that I'm just seeing tight code written by somebody while their mind was well soaked with the problem at hand, and not thinking about the guy who'd try to read it later (that guy most often being the writer himself, six months later... unless he's an APL hacker...).
So I take the code and try deobfuscate it. I copy/paste it into my text editor, and from there I rework it to split complex statements or expressions into smaller ones, and to assign intermediate values to variables with meaningful names. I do so iteratively, peeling off one layer of convolution at a time, until the result makes sense to me.
I did just that for your code. I believe you'll understand it easily by yourself in this shape. And me not being all too familiar with Python, I commented a few things for my own understanding at least as much as for yours.
This resulting code could use a bit of reordering and simplification, but I left in the form that would map most easily to the original four-liner, being the direct result of its expansion. Here we go :
data = [10,300,40,43,2,69]
increasingSubsequences = []
for i in range(len(data)):
# increasingSubsequences is a list of lists
# increasingSubsequences[j][-1] is the last element of increasingSubsequences[j]
candidatesForMoreIncrease = []
for j in range(i):
if data[i] > increasingSubsequences[j][-1]:
candidatesForMoreIncrease.append( increasingSubsequences[j] )
if len(candidatesForMoreIncrease) != 0:
nonEmpty = candidatesForMoreIncrease
else:
nonEmpty = [[]]
longestCandidate = max( nonEmpty, key=len ) # keep the LONGEST of the retained lists
# with the empty list included as a bottom element
# (stuff + [data[i]]) is like copying stuff and and then appending data[i] to the copy
increasingSubsequences.append( longestCandidate + [data[i]] )
print "All increasingSubsequences : ", increasingSubsequences
print "The result you expected : ", max(increasingSubsequences, key=len)
This looks like python to me.
max(arg1, arg2, *args[, key])
Returns the largest item in an iterable or the largest of two or more arguments.
If one positional argument is provided, iterable must be a non-empty iterable (such as a non-empty string, tuple or list). The largest item in the iterable is returned. If two or more positional arguments are provided, the largest of the positional arguments is returned.
The optional key argument specifies a one-argument ordering function like that used for list.sort(). The key argument, if supplied, must be in keyword form (for example, max(a,b,c,key=func)).
In this case it appears the variable l = [] is going to be appended with the largest iterable or the largest of 2 arguments from the stuff inside of the max function.
iterable 1:
[l[j] for j in range(i) if l[j][-1] < d[i]]
or
[[]]
Sometimes in words it helps too:
j in l for j in range i if j in l times -1 is less than i in d or [[]]
iterable 2:
key=len
Add [d[i]] to the one that returns the max result
Append this result to l = [] making l = [result]
Append simply adds an item to the end of the list; equivalent to a[len(a):] = [x].
I am stuck in making a loop that will eliminate the values(from the alist) that are below average.
Thanks for the help.
a=input("Enter a list of values separated by a coma : ")
alist=eval(a)
print("the list is : ",alist)
average = sum(alist)/len(alist)
print("the average is : ",average)
for i in alist:
if alist[i]<average:
alist.remove[i]
You are almost there. Instead of removing elements, select elements you want to retain instead:
alist = [a for a in alist if a>=average]
Your mistake here is that for i in alist: is iterating over list elements themselves, not indexes, so alist[i] is throwing an error (or returning nonsense).
For the "loop" you can use a filter and a lambda function.
above_average = list(filter(lambda x: x >= average, alist))
For the rest of your code, I suggest you clean it up to something which is safer (use of eval is very bad)
import ast
user_string = raw_input('input a list of numbers separated by a commas: ')
alist = list(ast.literal_eval(user_string)))
So, in all, I would write your code as something like this:
import ast
user_string = raw_input('input a list of numbers separated by a commas: ')
numbers = list(ast.literal_eval(user_string)))
average = sum(numbers)/len(numbers)
print('The numbers: {}'.format(numbers))
print('The average: {}'.format(average))
above_average = list(filter(lambda x: x >= average, numbers))
# now do what you want with the above_average numbers.
Other answers tell you how to do it. I'll tell you why it doesn't work:
You iterate over the list and, at the same time, modify it.
This leads to items being missed during the iteration.
Why?
Internally, the iteration works via an index to the list. So it is the same as doing
idx = 0
while True:
try:
i = alist[idx]
except IndexError:
break
idx += 1
if alist[i] < average:
alist.remove(i)
What happens if you are at the element #3, go to the next one and then remove #3? Right, the indexes of the remaining ones move down and you are pointing to the one which formerly was #5. The old #4 is skipped at this test.
(BTW, I don't know if you noticed, I have replaced your [] behind .remove with ().)
You are mixing two ways of iterating a list: By index, and by element. In your loop, i is not the index, but the element of the list itself, thus alist[i] won't work.
If you use the for x in somelist loop, then x is the element itself, not the index of the element. For iterating over the indices, you can use for i in range(len(somelist)), or you could use for i, x in enumerate(somelist) to loop over tuples of index and element.
Also note that removing elements from a list or other kinds of collections while you are looping them generally is a bad idea. Better create a copy of the list.
for x in list(alist): # creates a copy of alist
if x < average: # remember: x is the element itselt
alist.remove(x) # remove element x from list
But the way you do it (with eval of a comma-separated string of numbers), alist is a tuple, not a list, and thus has no remove method at all. Thus you either have to convert it to a list before (alist = list(eval(a)), or use one of the approaches given in the other answers, creating a new list using list comprehension or filter and retaining the "good" elements.
As a general principle for asking StackOverflow questions like this, you should always include example input and output -- show what happens, and what you expect to happen.
In this case, I believe there are two three problems with your code:
Edit: Third, but possibly most importantly, look at glglgl's answer. If you implement the two fixes I describe below, you'll still have one problem: your code won't necessarily remove all the items you want to remove, because it'll skip over some items.
First, you say alist[i], which grabs the element of alist at index i. But saying for i in alist makes i be successive elements in the list already. Example:
mylist = [1, 2, 4]
for i in mylist:
print(i)
Would give you the output:
1
2
4
If you instead said this (which is like what you wrote)
mylist = [1, 2, 4]
for i in mylist:
print(mylist[i])
It wouldn't work as you'd expect, because you'd get the element at index 1, the element at index 2, and then try to get the element at index 4, but that wouldn't exist. You'll get something like this:
2
4
IndexError: list index out of range
Second, your syntax for removing an element is wrong. You should use alist.remove(i) instead of alist.remove[i]. You want to call a function, so you use parentheses. The square brackets are for indexing and slicing.
I have two lists ref_list and data_list containing each a tuples with the first element being like a time in second and the second one being a random value as :
ref_list = [(1,value_ref_1),(3,value_ref_3),(4,value_ref_4), ... ]
data_list = [(1,value_dat_1),(2,value_dat_2),(4,value_dat_4), ... ]
I want to compute the difference of the second values as a function of time (first value of tuples). Wich means, a list of tuples which first value would be a time and the second the difference of second values. And I want it to be able to manage missing data in any of the two list using last time !
For the previous example, the result would be :
res_list = [(1,value_dat_1-value_ref_1),(2,value_dat_2-value_ref_1),(3,value_dat_2-value_ref_3),(4,value_dat_4-value_ref_4), ... ]
In this example, the tuple (2,value_dat_2-value_ref_1) was created with tuples (2,value_dat_2) and (1,value_ref_1) because a tuple with 2 as first was missing in ref_list. Same idea the other way around for (3,value_dat_2-value_ref_3)
I can't figure out how to do it with a list comprehension.
I hope I was clear enough.
Thanks a lot.
Ran the following additionally with two lists with 500k values each, 100mb/200mb (depending on generation parameters) stable memory usage
list_a = [(1,222),(2,444),(5,666),(10,888)]
list_b = [(1,111),(3,333),(7,555),(9,777),(10,888)]
list_c = []
i = 1
a = None
b = None
def get_new(a, for_time):
if len(a) == 0:
raise IndexError
# in the future
if a[0][0] > for_time:
return None
return a.pop(0)
list_a_exhausted = False
list_b_exhausted = False
while True:
try:
a = get_new(list_a,i) or a
except IndexError:
list_a_exhausted = True
try:
b = get_new(list_b,i) or b
except IndexError:
list_b_exhausted = True
if list_a_exhausted and list_b_exhausted:
break
list_c.append([(i,b[1]-a[1])])
i = i + 1
Edit 1 :
IndexError : if both list have the same length, you shouldn't have an index error.
data_list[i] will give the ith element of of data_list, regardless of its content.
And when you pop a value, from a python list(), it 'moves' the indexes, so you don't have an index gap (unlike other languages). Or maybe I didn't understand well your concern.
Missing data: yes, yes.
So you need to return multiple values in case of a missing one: the upper and the lower bounds
[(elt[0],data_list[i][1]-elt[1]) if data_list[i][0]==elt[0] else ((elt[0],data_list[i][1]-ref_list[i-1][1]),(elt[0],data_list[i][1]-ref_list[i+1][1])) for i,elt in enumerate(ref_list)]
This way, if a value is missing, it'll go search for the previous value and the next value, so you could have the bounds of the missing value. I have no other choice than returning for the 'else' tuples in another structure, 'cause I can return only one 'value' at each turn. ( or face a SyntaxError : invalid syntax at the 'for')
Even if you may need these tuples of tuples (to detect a value is missing), you might want to know another solution - an explicit generator, there.
def generator_stuff(data_list,ref_list):
for i,elt in enumerate(ref_list):
if data_list[i][0]==elt[0]:
yield (elt[0],data_list[i][1]-elt[1])
else:
yield (elt[0],data_list[i][1]-ref_list[i-1][1])
yield (elt[0],data_list[i][1]-ref_list[i+1][1])
I have absolutely no idea of the performance of this, but as it return each tuple individually, you won't have tuples of tuples.