How do I iterate between lists of different lenghts? - python

I have two lists which I need to Iterate together. Let me show how:
listA=[1,2,3,4]
listB=["A","B","C"]
From those lists I would like to have this list
ListC=("1A","2B","3C","4A")
And even make a longer list in which I can loop the same iteration
ListC=("1A","2B","3C","4A","1B","2C","3A","4C".... and so on)
I couldn`t find any tutorial online that would answer this question
Thanks.

Use zip and itertools.cycle:
>>> from itertools import cycle
>>> listA = [1, 2, 3, 4]
>>> listB = ["A", "B", "C"]
>>> [f'{x}{y}' for x, y in zip(listA, cycle(listB))]
['1A', '2B', '3C', '4A']
# listA: 1 2 3 4
# cycle(listB): "A" "B" "C" "A" "B" "C" ...
cycle endlessly cycles through the elements of its argument; zip stops iterating after its shorter argument is exhausted.
You can use cycle with both lists, but the result will be an infinite sequence of values; you'll need to use something like itertools.islice to take a finite prefix of the result.
>>> from itertools import cycle, islice
>>> [f'{x}{y}' for x, y in islice(zip(cycle(listA), cycle(listB)), 8)]
['1A', '2B', '3C', '4A', '1B', '2C', '3A', '4B']
# cycle(listA): 1 2 3 4 1 2 3 4 1 2 3 4 1 ...
# cycle(listB): "A" "B" "C" "A" "B" "C" "A" "B" "C" "A" "B" "C" "A" ...
# Note that the result itself is a cycle of 12 unique elements, because
# the least common multiple (LCM) of 3 and 4 is 12.

You can use modulo to take care of this kind of problem. Here's code to repeat this 100 times:
l1 = [1, 2, 3, 4]
l2 = ['a', 'b', 'c']
result = []
for i in range(100):
result.append(str(l1[i % len(l1)]) + l2[i % len(l2)])
print (result)

listA=[1,2,3,4]
listB=["A","B","C"]
listC=[]
for a in listA:
index = listA.index(a)
if listA.index(a) > len(listB) - 1:
if listC[-1][1] != listB[-1]:
index = listB.index(listC[-1][1]) + 1
else:
index = 0
listC.append(str(a)+listB[index])
print(listC)

Related

How get count of value repeated in list at index position 1?

How to count number of values repeated in list at first position and do sum based on that
My input :
[[2, 1], [2, 1], [2, 1], [1, 2]]
Note : my list will contain 2 OR 1 in first index[0]
In above the 2 is repeated the maximum number of times so my sum should be like get its second value of all and do sum and display
2 , 1 -> 1 +
2 , 1 -> 1 +
2 , 1 -> 1
----------------------
2 , 3
So output would be : 2 , 3
How to achieve above output from given Input
In my code not able to implement such logic
cnt=0
m[0]=0
for m in my_list:
if m[0] == 2
cnt+=1
v=m[1]
print(m[0],v[1])
Try:
#create a list with just the 0th elements
keys = [i[0] for i in l]
#get the key that has the maximum count
max_key = max(keys, key=keys.count)
#sum the 1st element for all sublists where the 0th element is the same as the max_key
>>> max_key, sum(i[1] for i in l if i[0]==max_key)
(2, 3)
In one line (not recommended as it's not very readable):
>>> max([i[0] for i in l], key=[i[0] for i in l].count), sum(i[1] for i in l if i[0]==max([i[0] for i in l], key=[i[0] for i in l].count))
(2, 3)
Suppose we know 2 is the most common 1st item, here's a one liner list comprehension to make the sum:
[2, sum(x[1] for x in l if x[0]==2)]
# ^ ^ ^
# | | \-- if 1st item is 2
# | \-- extract 2nd item
# \-- sum all the 2nd items
To find the most common 1st item, we can use collections.Counter:
from collections import Counter
Counter(x[0] for x in l).most_common(1) # [(2, 3)]
Put everything together:
n = Counter(x[0] for x in l).most_common(1)[0][0]
[n, sum(x[1] for x in l if x[0]==n)] # [2, 3]

Python splitting an int based on the char length

I’m new to python and would like to do a simple function. I’d like to read the input array and if the value is more than 4 digits, to then split it then print the first value then the second value.
I’m having issues splitting the number and getting rid of 0’s inbetween; so for example 1006, would become 1, 6.
Input array:
a = [ 1002, 2, 3, 7 ,9, 15, 5992]
Desired output in console:
1, 2
2
3
7
9
15
59,92
You can abstract the splitting into a function and then use a list comprehension to map that function over the list. The following can be tweaked (it matches more of what you had before one of your edits). It can be tweaked of course:
def split_num(n):
s = str(n)
if len(s) < 4:
return 0, n
else:
a,b = s[:2], s[2:]
if a[1] == '0': a = a[0]
return int(a), int(b)
nums = [1002, 2, 3, 7 ,9, 15, 5992]
result = [split_num(n) for n in nums]
for a,b in result:
print(a,b)
Output:
1 2
0 2
0 3
0 7
0 9
0 15
59 92
If you just want a list of the non-zero digits in the original list, you can use this:
a = [ 1002, 2, 3, 7 ,9, 15, 5992]
strings = [str(el) for el in a]
str_digits = [char for el in strings for char in el if char != '0']
and if you want the digits as ints, you can do:
int_digits = [int(el) for el in str_digits]
or go straight to
int_digits = [int(char) for el in strings for char in el if char != '0']
I'm not sure what the logic is behind your desired output is, though, so if this isn't helpful I'm sorry.

Remove last digit from String depending on length

I am trying to remove the last digit in the df[4] string if the string is over 5 digits.
I tried adding .str[:-1] to df[4]=df[4].astype(str) this removes the last digit from every string in the dataframe.
df[3]=df[3].astype(str)
df[4]=df[4].astype(str).str[:-1]
df[5]=df[5].astype(str)
I tried several different combinations of if statements but none have worked.
I'm new to python and pandas so any help is appreciated
You can filter first on the string length:
condition = df[4].astype(str).str.len() > 5
df.loc[condition, 4]=df.loc[condition, 4].astype(str).str[:-1]
For example:
>>> df
4
0 1
1 11
2 111
3 1111
4 11111
5 111111
6 1111111
7 11111111
8 111111111
>>> condition = df[4].astype(str).str.len() > 5
>>> df.loc[condition, 4]=df.loc[condition, 4].astype(str).str[:-1]
>>> df
4
0 1
1 11
2 111
3 1111
4 11111
5 11111
6 111111
7 1111111
8 11111111
If these are natural integers, it is however more efficient to divide by 10:
condition = df[4].astype(str).str.len() > 5
df.loc[condition, 4]=df.loc[condition, 4] // 10
Accessing Elements of a Collection
>>> x = "123456"
# get element at index from start
>>> x[0]
'1'
# get element at index from end
>>> x[-1]
'6'
# get range of elements from n-index to m-index
>>> x[0:3]
'123'
>>> x[1:-2]
'234'
>>> x[-4:-2]
'34'
# get range from/to index with open end/start
>>> x[:-2]
'1234'
>>> x[4:]
'56'
List Comprehension Syntax
I haven't see the pythons list comprehension syntax which really cool and easy.
# input data frame with variable string length 1 to n
df = [
'a',
'ab',
'abc',
'abcd',
'abcdf',
'abcdfg',
'abcdfgh',
'abcdfghi',
'abcdfghij',
'abcdfghijk',
'abcdfghijkl',
'abcdfghijklm'
]
# using list comprehension syntax: [element for element in collection]
df_new = [
# short hand if syntax: value_a if True else value_b
r if len(r) <= 5 else r[0:5]
for r in df
]
Now df_new contains only string up to a length of 5:
[
'a',
'ab',
'abc',
'abcd',
'abcdf',
'abcdf',
'abcdf',
'abcdf',
'abcdf',
'abcdf',
'abcdf',
'abcdf'
]
cause [-1]removes last numbers or change number to -1
try str df[4]=-1

Iterate though list on each for loop iteration

I'm working on the following code:
mylist = [1,2,3,4,5,6,7,8,9,10.....]
for x in range(0, len(mylist), 3):
value = mylist[x:x + 3]
print(value)
Basically, I'm taking 3 items in mylist at a time, the code is bigger than that, but I'm doing a lot of things with them returning a value from it, then it takes the next 3 items from mylist and keep doing it till the end of this list.
But now I have a problem, I need to identify each iteration, but they follow a rule:
The first loop are from A, the second are from B and the third are from C.
When it reaches the third, it starts over with A, so what I'm trying to do is something like this:
mylist[0:3] are from A
mylist[3:6] are from B
mylist[6:9] are from C
mylist[9:12]are from A
mylist[12:15] are from B......
The initial idea was to implement a identifier the goes from A to C, and each iteration it jumps to the next identifier, but when it reaches C, it backs to A.
So the output seems like this:
[1,2,3] from A
[4,5,6] from B
[6,7,8] from C
[9,10,11] from A
[12,13,14] from B
[15,16,17] from C
[18,19,20] from A.....
My bad solution:
Create identifiers = [A,B,C] multiply it by the len of mylist -> identifiers = [A,B,C]*len(mylist)
So the amount of A's, B's and C's are the same of mylist numbers that it needs to identify. Then inside my for loop I add a counter that adds +1 to itself and access the index of my list.
mylist = [1,2,3,4,5,6,7,8,9,10.....]
identifier = ['A','B','C']*len(mylist)
counter = -1
for x in range(0, len(mylist), 3):
value = mylist[x:x + 3]
counter += 1
print(value, identifier[counter])
But its too ugly and not fast at all. Does anyone know a faster way to do it?
Cycle, zip, and unpack:
mylist = [1,2,3,4,5,6,7,8,9,10]
for value, iden in zip(mylist, itertools.cycle('A', 'B', 'C')):
print(value, iden)
Output:
1 A
2 B
3 C
4 A
5 B
6 C
7 A
8 B
9 C
10 A
You can always use a generator to iterate over your identifiers:
def infinite_generator(seq):
while True:
for item in seq:
yield item
Initialise the identifiers:
identifier = infinite_generator(['A', 'B', 'C'])
Then in your loop:
print(value, next(identifier))
Based on Ignacio's answer fitted for your problem.
You can first reshape your list into a list of arrays containing 3 elements:
import pandas as pd
import numpy as np
import itertools
mylist = [1,2,3,4,5,6,7,8,9,10]
_reshaped = np.reshape(mylist[:len(mylist)-len(mylist)%3],(-1,3))
print(_reshaped)
[[1 2 3]
[4 5 6]
[7 8 9]]
Note that it works since your list contains multiple of 3 elements (so you need to drop the last elements in order to respect this condition, mylist[:len(mylist)-len(mylist)%3]) - Understanding slice notation
See UPDATE section for a reshape that fits to your question.
Then apply Ignacio's solution on the reshaped list
for value, iden in zip(_reshaped, itertools.cycle(('A', 'B', 'C'))):
print(value, iden)
[1 2 3] A
[4 5 6] B
[7 8 9] C
UPDATE
You can use #NedBatchelder's chunk generator to reshape you array as expected:
def chunks(l, n):
"""Yield successive n-sized chunks from l."""
for i in range(0, len(l), n):
yield l[i:i + n]
mylist = [1,2,3,4,5,6,7,8,9,10]
_reshaped = list(chunks(mylist, 3))
print(_reshaped)
[[1 2 3]
[4 5 6]
[7 8 9]
[10]]
Then:
for value, iden in zip(_reshaped, itertools.cycle(('A', 'B', 'C'))):
print(value, iden)
[1 2 3] A
[4 5 6] B
[7 8 9] C
[10] A
Performances
Your solution : 1.32 ms ± 94.3 µs per loop
With a reshaped list : 1.32 ms ± 84.6 µs per loop
You notice that there is no sensitive difference in terms of performances for an equivalent result.
You could create a Generator for the slices:
grouped_items = zip(*[seq[i::3] for i in range(3)])

How does this python nested for loops work?

Can anyone explain why the output of the following nested loop is {1:6, 2:6, 3:6}?
>>> {x:y for x in [1, 2, 3] for y in [4, 5, 6]}
{1:6, 2:6, 3:6}
my_dict = {x:y for x in [1,2,3] for y in [4,5,6]}
is the same is creating it as follows
my_dict = {}
for x in [1,2,3]:
for y in [4,5,6]:
my_dict[x] = y
Which would look like this if you unroll the loops:
my_dict = {}
my_dict[1] = 4
my_dict[1] = 5
my_dict[1] = 6
my_dict[2] = 4
my_dict[2] = 5
my_dict[2] = 6
my_dict[3] = 4
my_dict[3] = 5
my_dict[3] = 6
You are effectively inserting nine key value pairs into the dictionary. However, each time you insert a pair with a key that already exists it overwrites the previous value. Thus you only ended up with the last insert for each key where the value was six.
The difference is you are making a dictionary vs a list. In your own example, you are effectively constructing a dictionary and because you set a different value for the same key 3 times, the last value sticks.
You are effectively doing:
dict[1] = 4
dict[1] = 5
dict[1] = 6
...
dict[3] = 4
dict[3] = 5
dict[3] = 6
So the last value sticks.
If the expectation was to create {1:4, 2:5, 3:6}, try this:
{x[0]:x[1] for x in zip([1,2,3], [4,5,6])}

Categories

Resources