List Strange Unexplained [closed] - python

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 9 years ago.
Improve this question
Can you please explain this to me, I'm completely lost here.
This is my code:
def ff(L):
for a in L:
k = L.index(a)
print(k)
b = L.pop(k)
g = b
print(g)
L.insert(k,g)
return L
This is the output:
>>> L = [12,13,14]
>>> ff(L)
0
12
1
13
2
14
[12, 13, 14]
But when i do this:
def ff(L):
for a in L:
k = L.index(a)
print(k)
b = L.pop(k)
g = b + 1
print(g)
L.insert(k,g)
return L
output:
>>> L = [12,13,14]
>>> ff(L)
0
13
0
14
0
15
[15, 13, 14]
Why So?

It' quite obvious. You can add more print to your code to see reasons yourself:
>>> def ff(L):
... for a in L:
... k = L.index(a)
... print 'value', a, 'at', k, 'pos in', L,
... b = L.pop(k)
... g = b + 1
... print 'list after pop', L,
... L.insert(k,g)
... print 'inserted value', g, 'list after ins', L
... return L
...
>>> ff(L)
value 12 at 0 pos in [12, 13, 14] list after pop [13, 14] inserted value 13 list after ins [13, 13, 14]
value 13 at 0 pos in [13, 13, 14] list after pop [13, 14] inserted value 14 list after ins [14, 13, 14]
value 14 at 0 pos in [14, 13, 14] list after pop [13, 14] inserted value 15 list after ins [15, 13, 14]
[15, 13, 14]
So you basically look for a value, pop it, insert value+1 at first position, look for value+1 and further.

I am not sure what's the question here, but as you incremented the value in the first iteration the item at 0th index becomes 13. And during the second iteration L.index(13) returns the index 0 again, so in the second iteration you modified the item at index 0 again to 14. And this goes on...
def ff(L):
for a in L:
print 'List', L, 'searching for', a, 'found at', L.index(a)
k = L.index(a)
b = L.pop(k)
g = b + 1
L.insert(k,g)
return L
L = [12,13,14]
print ff(L)
Output:
List [12, 13, 14] searching for 12 found at 0
List [13, 13, 14] searching for 13 found at 0
List [14, 13, 14] searching for 14 found at 0
[15, 13, 14]
So, list.index() always returns the index of first match found, that's why in the second case the item at 0th index gets incremented.
A simple solution to increment all values by 1 will be:
>>> L = [12,13,14]
>>> [x+1 for x in L]
[13, 14, 15]

Related

Pandas compare items in list in one column with single value in another column

Consider this two column df. I would like to create an apply function that compares each item in the "other_yrs" column list with the single integer in the "cur" column and keeps count of each item in the "other_yrs" column list that is greater than or equal to the single value in the "cur" column. I cannot figure out how to enable pandas to do this with apply. I am using apply functions for other purposes and they are working well. Any ideas would be very appreciated.
cur other_yrs
1 11 [11, 11]
2 12 [16, 13, 12, 9, 9, 6, 6, 3, 3, 3, 2, 1, 0]
4 16 [15, 85]
5 17 [17, 17, 16]
6 13 [8, 8]
Below is the function I used to extract the values into the "other_yrs" column. I am thinking I can just insert into this function some way of comparing each successive value in the list with the "cur" column value and keep count. I really only need to store the count of how many of the list items are <= the value in the "cur" column.
def col_check(col_string):
cs_yr_lst = []
count = 0
if len(col_string) < 1: #avoids col values of 0 meaning no other cases.
pass
else:
case_lst = col_string.split(", ") #splits the string of cases into a list
for i in case_lst:
cs_yr = int(i[3:5]) #gets the case year from each individual case number
cs_yr_lst.append(cs_yr) #stores those integers in a list and then into a new column using apply
return cs_yr_lst
The expected output would be this:
cur other_yrs count
1 11 [11, 11] 2
2 12 [16, 13, 12, 9, 9, 6, 6, 3, 3, 3, 2, 1, 0] 11
4 16 [15, 85] 1
5 17 [17, 17, 16] 3
6 13 [8, 8] 2
Use zip inside a list comprehension to zip the columns cur and other_yrs and use np.sum on boolean mask:
df['count'] = [np.sum(np.array(b) <= a) for a, b in zip(df['cur'], df['other_yrs'])]
Another idea:
df['count'] = pd.DataFrame(df['other_yrs'].tolist(), index=df.index).le(df['cur'], axis=0).sum(1)
Result:
cur other_yrs count
1 11 [11, 11] 2
2 12 [16, 13, 12, 9, 9, 6, 6, 3, 3, 3, 2, 1, 0] 11
4 16 [15, 85] 1
5 17 [17, 17, 16] 3
6 13 [8, 8] 2
You can consider explode and compare then group on level=0 and sum:
u = df.explode('other_yrs')
df['Count'] = u['cur'].ge(u['other_yrs']).sum(level=0).astype(int)
print(df)
cur other_yrs Count
1 11 [11, 11] 2
2 12 [16, 13, 12, 9, 9, 6, 6, 3, 3, 3, 2, 1, 0] 11
4 16 [15, 85] 1
5 17 [17, 17, 16] 3
6 13 [8, 8] 2
If columns contain millions of records in both of the dataframes and one has to compare each element in first column with all the elements in the second column then following code might be helpful.
for element in Dataframe1.Column1:
Dataframe2[Dateframe2.Column2.isin([element])]
Above code snippet will return one by one specific rows of dataframe2 where element from dataframe1 is found in dataframe2.column2.

multiple iteration in a single for loop [duplicate]

This question already has answers here:
How do I iterate through two lists in parallel?
(8 answers)
Closed 5 years ago.
i have,
list1 = [1, 2, 3, 4, 5]
list2 = [7, 8, 9, 14, 25, 36]
list3 = [43, 65]
and what i want to achieve is
1 7 43
2 8 65
3 9
4 14
5 25
36
i have tried looking in methods like itertools and multiprocessiongbut none of them helps,
is there a way i can achiver this in a single for loop like...
for I, J, K inb list1, list2, list3:
print('{} {} {}'.format(I, J, K))
any help?
EDIT
zip function gets the least number of elements from the lists, if i use zip the output will be
1 7 43
2 8 65
You can try this using itertools:
import itertools
list1 = [1, 2, 3, 4, 5]
list2 = [7, 8, 9, 14, 25, 36]
list3 = [43, 65]
for a, b, c in itertools.izip_longest(list1, list2, list3):
print a, b, c

Pick subset from list at random and maintain equal number of picks overall in python

given a list of strings like so (in reality I have a much longer list but I'll keep it short for here):
items=['fish','headphones','wineglass','bowtie','cheese','hammer','socks']
I would like to pick a subset, say 3, of this list randomly so that items can only get picked once. This is easy enough using the following:
import itertools
import random
def random_combination(iterable, r):
"Random selection from itertools.combinations(iterable, r)"
pool = tuple(iterable)
n = len(pool)
indices = sorted(random.sample(xrange(n), r))
return tuple(pool[i] for i in indices)
items=['fish','headphones','wineglass','bowtie','cheese','hammer','socks']
randomPick=random_combination(items,3)
Next, to be a pain, I don't want to do this just once, but several times say 10 times. The final product would be 10 lists of randomly-only-picked-once items with the constraint that over those 10 lists items are presented an equal amount of times across lists. I'd like to avoid the "socks" to be picked up 10 times and the "hammer" only once for example.
This is the step that I'm stuck with, I simply don't know enough programming or enough about the available functions in python to perform such a thing.
Can anyone help?
The following code might help. It pops a random element until (a copy of) iterable is empty, then starts over from the entire list. The downside is every item is picked once before a single item can be picked a second time. However, as you can see from the output, the distribution of items ends up about equal.
import random
def equal_distribution_combinations(iterable, n, csize):
"""
Yield 'n' lists of size 'csize' containing distinct random elements
from 'iterable.' Elements of 'iterable' are approximately evenly
distributed across all yielded combinations.
"""
i_copy = list(iterable)
if csize > len(i_copy):
raise ValueError(
"csize cannot exceed len(iterable), as elements could not distinct."
)
for i in range(n):
comb = []
for j in range(csize):
if not i_copy:
i_copy = list(iterable)
randi = random.randint(0, len(i_copy) - 1)
# If i_coppy was reinstantiated it would be possible to have
# duplicate elements in comb without this check.
while i_copy[randi] in comb:
randi = random.randint(0, len(i_copy) - 1)
comb.append(i_copy.pop(randi))
yield comb
Edit
Apologies for Python 3. The only change to the function for Python 2 should be range -> xrange.
Edit 2 (answering comment question)
equal_distribution_combinations should result in an even distribution for any n, csize, and length of iterable, as long as csize does not exceed len(iterable) (as the combination elements could not be distinct).
Here's a test using the specific numbers in your comment:
items = range(30)
item_counts = {k: 0 for k in items}
for comb in equal_distribution_combinations(items, 10, 10):
print(comb)
for e in comb:
item_counts[e] += 1
print('')
for k, v in item_counts.items():
print('Item: {0} Count: {1}'.format(k, v))
Output:
[19, 28, 3, 20, 2, 9, 0, 25, 27, 12]
[29, 5, 22, 10, 1, 8, 17, 21, 14, 4]
[16, 13, 26, 6, 23, 11, 15, 18, 7, 24]
[26, 14, 18, 20, 16, 0, 1, 11, 10, 2]
[27, 21, 28, 24, 25, 12, 13, 19, 22, 6]
[23, 3, 8, 4, 15, 5, 29, 9, 7, 17]
[11, 1, 8, 28, 3, 13, 7, 26, 16, 23]
[9, 29, 14, 15, 17, 21, 18, 24, 12, 10]
[19, 20, 0, 2, 25, 5, 22, 4, 27, 6]
[12, 13, 24, 28, 6, 7, 26, 17, 25, 23]
Item: 0 Count: 3
Item: 1 Count: 3
Item: 2 Count: 3
Item: 3 Count: 3
Item: 4 Count: 3
Item: 5 Count: 3
Item: 6 Count: 4
Item: 7 Count: 4
Item: 8 Count: 3
Item: 9 Count: 3
Item: 10 Count: 3
Item: 11 Count: 3
Item: 12 Count: 4
Item: 13 Count: 4
Item: 14 Count: 3
Item: 15 Count: 3
Item: 16 Count: 3
Item: 17 Count: 4
Item: 18 Count: 3
Item: 19 Count: 3
Item: 20 Count: 3
Item: 21 Count: 3
Item: 22 Count: 3
Item: 23 Count: 4
Item: 24 Count: 4
Item: 25 Count: 4
Item: 26 Count: 4
Item: 27 Count: 3
Item: 28 Count: 4
Item: 29 Count: 3
As can be seen, the items are evenly distributed.
i would do something like this:
items = set(items)
res = []
for _ in xrange(10):
r = random.sample(items, 3)
res.append(r)
items -= set(r)
all this does is grab 3 elements, store them, and then subtract them from the original list so they can't be selected again.
Ok in the end I resorted in doing the following.
It is a more constrained implementation where I set the number of times I want to see an item repeat, for example over the 10 lists I want each items to be picked 5 times:
List = ['airplane',
'fish',
'watch',
'balloon',
'headphones',
'wineglass',
'bowtie',
'guitar',
'desk',
'bottle',
'glove'] #there is more in my final list but keeping it short here
numIters = 5
numItems = len(List)
finalList=[]
for curList in range(numIters):
random.shuffle(List)
finalList.append(List[0 : numItems/2]) #append first list
finalList.append(List[numItems/2 : -1]) #append second list
return finalList

Changing a list item without changing original values

I'm doing some practice problems and one program is asking me to change numbers in the list to 99, only if the number previously is even. I'm running into the trouble that when I change the number to 99, when it moves on the the next number, it checks to see if 99 is even, which I don't want, I want it to check the original value I had there, not what I changed it to .
d = [9, 8, 2, 15, 6, 33, 10, 4]
i = 1
while i < len(d) - 1 :
if d[i-1]%2 == 0 :
d[i] = 99
i = i + 1
print d
returns [9, 8, 99, 15, 6, 99, 10, 4]
I want it to return [9,8,99,99,6,99,10,99]
How would I add 99 into the original list without changing its original value if that makes any sense? List methods like pop or insert can not be used.
Try this.
d = [9, 8, 2, 15, 6, 33, 10, 4]
for i in reversed(xrange(1, len(d))):
d[i] = 99 if d[i - 1] % 2 == 0 else d[i]
I would advice to iterate descending:
d = [9, 8, 2, 15, 6, 33, 10, 4]
for x in xrange(len(d)-1, 0, -1):
if d[x - 1] % 2 == 0:
d[x] = 99
print d
In this case next iteration will operate not changed values.
Or You can create new list
or You can add variable for previous value and use it in if statement
d = [9, 8, 2, 15, 6, 33, 10, 4]
previous = d[0]
for i, x in enumerate(d[1:]):
if previous % 2 == 0 :
previous = d[i]
d[i] = 99
else:
previous = d[i]
print d
search the list backwards
see last and before last value (n-1, n-2)
change the last value if needed
move to previous values (n-2,n-3)
repeat until whole list is updated
Try this:
d = [9, 8, 2, 15, 6, 33, 10, 4]
prev=d[0]
for i,j in enumerate(d):
if prev%2==0:
prev=j
d[i]=99
else:
prev=j
print d

How to return 2 separate lists from a single list?

If have this list here:
[25, 8, 22, 9]
How can I make the program create 2 seperate lists, and print them both? One should contain all the numbers less than 20, and the other needs to contain all the numbers greater than 20. The final print result should be displayed like this: [8, 9], [25, 22]
>>> predicates = lambda x:x<20, lambda x:x>20
>>> print [filter(pred, [25, 8, 22, 9]) for pred in predicates]
[[8, 9], [25, 22]]
Use list comprehensions:
>>> L = [25, 8, 22, 9]
>>> [x for x in L if x < 20]
[8, 9]
>>> [x for x in L if x > 20]
[25, 22]
def print_split_list(raw_list, split_value):
lower_list = [v for v in raw_list if v < split_value]
upper_list = [v for v in raw_list if v >= split_value]
print lower_list, upper_list
print_split_list([25, 8, 22, 9], 20) # => [8, 9] [25, 22]
a = [25, 8, 22, 9]
print [x for x in a if x > 20]
print [x for x in a if x < 20]
You use here list comprehensions. List comprehension looks like:
[ f(x) for x in a if cond(x) ]
That means: produce me a list that consists of f(x) for each element of x for that cond(x) is True.
In our case f(x)is simply x. And cond(x) is x > 20 or x < 20 (please note also that if you have 20s in your list, they will disappear from the result).
If it is a homework you can solve the task in more low-level way:
a = [25, 8, 22, 9]
list1 = []
list2 = []
for elem in a:
if elem > 20:
list1.append(elem)
if elem < 20:
list2.append(elem)
print list1
print list2
Here you iterate through the list and check its elements.
That elements that are greater than 20 you append to one list; and that that are lesser thatn 20 — to the other.
Note: the assumes you want twenty in listTwo
listOne = [x for x in yourList if x < 20]
listTwo = [x for x in yourList if x >= 20]
print listOne
print listTwo
Although you should use list comprehensions you might be interested in the for loop approach if you are starting with python
listOne = []
listOne = []
for x in yourList:
if x < 20:
listOne.append(x)
else:
listTwo.append(x)
li = [25, 8, 22, 9]
li.sort()
for i, x in enumerate(li):
if x > 20:
print li[:i]
print li[i:]
break

Categories