python itertools groupby with filter usage - python

I have a list = [1, 2, 3, 3, 6, 8, 8, 10, 2, 5, 7, 7]
I am trying to use groupby to convert it into
1
2
3
3
6
8,8
10
2,
5
7,7
Basically, anything greater then 6, I like to group them, otherwise I want to keep them ungrouped.
Any hint on how I can do this with itertool groupby
My code currently:
for key, group in it.groupby(numbers, lambda x: x):
f = list(group)
if len(f) == 1:
split_list.append(group[0])
else:
if (f[0] > 6): #filter condition x>6
for num in f:
split_list.append(num + 100)
else:
for num in f:
split_list.append(num)

You can use itertools.groupby to group all elements greater than 6 and with groups of length greater than 1. All other elements remain ungrouped.
If we want groups as standalone lists, we can use append. If we want groups flattened, we can use extend.
from itertools import groupby
lst = [1, 2, 3, 3, 6, 8, 8, 10, 2, 5, 7, 7]
result = []
for k, g in groupby(lst):
group = list(g)
if k > 6 and len(group) > 1:
result.append(group)
else:
result.extend(group)
print(result)
Output:
[1, 2, 3, 3, 6, [8, 8], 10, 2, 5, [7, 7]]

You can use flatten() function shown below.
This function is modified version from https://stackoverflow.com/a/40857703/501852
The function below is a generator object (python ver >= 3.8).
Added conditioning function as input parameter.
from typing import Iterable
def flatten(items, cond_func=lambda x: True):
"""Yield items from any nested iterable"""
for x in items:
if isinstance(x, Iterable) \
and not isinstance(x, (str, bytes)) \
and cond_func(x): # Conditioning function
yield from flatten(x)
else:
yield x
Then you can use list comprehension variant:
res = flatten( [list(g) for k, g in groupby(lst)],
cond_func = lambda x: x[0] <= 6 or len(x) == 1)
# Conditions to flatten
print(res)
And you can use generator variant:
res = flatten( (list(g) for k, g in groupby(lst)),
cond_func = lambda x: x[0] <= 6 or len(x) == 1)
# Conditions to flatten
print(list(res))
Output is
[1, 2, 3, 3, 6, [8, 8], 10, 2, 5, [7, 7]]

Related

Remove specific words within a list with over twice occurance python

Here is a list A=[1,1,1,2,2,2,4,5,6,6,7,7,7]
Can we have an algorithm that modifies all the numbers that occur more than twice to be maximized twice in list A?
e.g. list new_A=[1,1,2,2,4,5,6,6,7,7]
I have tried the conditions to separate:
if Counter(list A)[num]>2:```
You can use groupby and islice:
from itertools import groupby, islice
lst = [1,1,1,2,2,2,4,5,6,6,7,7,7]
output = [x for _, g in groupby(lst) for x in islice(g, 2)]
print(output) # [1, 1, 2, 2, 4, 5, 6, 6, 7, 7]
You can do something like this:
from collections import Counter
max_accurance =2
list_A=[1,1,1,2,2,2,4,5,6,6,7,7,7]
d = dict(Counter(list_A))
new_list=[]
for k,v in d.items():
if v>=max_accurance:
new_list.extend([k]*max_accurance)
else:
new_list.append(k)
output
[1, 1, 2, 2, 4, 5, 6, 7, 7]
Most compressed way I could think of:
import operator as op
A=[1,1,1,2,2,2,4,5,6,6,7,7,7]
B = []
for elem in set(A):
B.extend([elem, elem]) if op.countOf(A, elem) > 2 else B.extend([elem])
Output:
[1, 1, 2, 2, 4, 5, 6, 7, 7]

How to zip several undefined different lenght lists in python?

I am trying to write a function that get a list of lists with different length as input and return the zipped result.
What I am looking for is that to expand my code below to any amount of lists. (I cannot use Zip Longest function since I am trying to do this on our system that does not have most of python functions including
zip longest function)
Here is my code:
a = [[1,2,3,4],[5,6],[7,8,9]]
def myzip(a):
temp1=[]
temp2=[]
temp3=[]
lens=[]
t=1
for i in a:
if(t==1):
temp1=i
lens.append(len(temp1))
t+=1
elif(t==2):
temp2=i
lens.append(len(temp2))
t+=1
elif(t==3):
temp3=i
lens.append(len(temp3))
for i in range(max(lens)):
if(i<len(temp1)):
print(temp1[i])
if(i<len(temp2)):
print(temp2[i])
if(i<len(temp3)):
print(temp3[i])
myzip(a)
Output:
1
5
7
2
6
8
3
9
4
This function works only for 3 lists because I am using Temp lists in order to achieve the zipped result But I want to make this code works for any number of lists. for example I able to run for [[1,2,3,4],[5,6],[7,8,9],[11,33]] or [[1,2,3,4],[5,6]] or [[1,2,3,4],[5,6],[7,8,9],...,[25,22]]
How about this:
from itertools import zip_longest
lists = [[1, 2, 3, 4], [5, 6], [7, 8, 9], [11, 33]]
for item in [x for t in zip_longest(*lists) for x in t]:
if item is not None:
print(item)
Output:
1
5
7
11
2
6
8
33
3
9
4
Or to just get them as a list:
items = [x for t in zip_longest(*lists) for x in t if x is not None]
Note: #MarkM made a worthwhile remark - if your source data contains None, this approach will have a problem in that it will filter them out. You should tell zip_longest to use a different fillvalue in that case, that does not show up in your data. For example:
items = [x for t in zip_longest(*lists, fillvalue='') for x in t if x is not None]
If you cannot import itertools for very specific reasons (as mentioned in the comments), you could just use the implementation shown in the documentation (https://docs.python.org/3/library/itertools.html#itertools.zip_longest):
def repeat(object, times=None):
if times is None:
while True:
yield object
else:
for i in range(times):
yield object
def zip_longest(*args, fillvalue=None):
iterators = [iter(it) for it in args]
num_active = len(iterators)
if not num_active:
return
while True:
values = []
for i, it in enumerate(iterators):
try:
value = next(it)
except StopIteration:
num_active -= 1
if not num_active:
return
iterators[i] = repeat(fillvalue)
value = fillvalue
values.append(value)
yield tuple(values)
Something like this could also work for you:
def flatten_list(my_list):
flat_list = []
for element in my_list:
if type(element) is list: #if element is a list, iterate through it
for item in element:
flat_list.append(item)
else:
flat_list.append(element)
return flat_list
nested_list = [[1, 2, 3, 4], [5, 6, 7], [8, 9, 10],[1,2,3],[1,2,4],[4,6,7,8]]
print('Original List', nested_list)
print('Flat List', flatten_list(nested_list))
Output
Original List [[1, 2, 3, 4], [5, 6, 7], [8, 9, 10], [1, 2, 3], [1, 2, 4], [4, 6, 7, 8]]
Flat List [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 1, 2, 4, 4, 6, 7, 8]

Filtering indexes of list

I have the following list of numbers:
List = [1, 2, 3, 4, 5, 6, 15]
I want the indexes of those numbers which are multiple of n, so I do:
def indexes(List, n):
# to enumerate the numbers
E = enumerate(List)
# filtering tuples
F = list(filter(lambda x: x[1] % n == 0, E))
return [ i[0] for i in F]
indexes(List, 2)
[1, 3, 5]
That's ok, but what happens when I add the variable m?
def Index( L, n, m):
# enumeration
E = enumerate(L)
# filtering tuples
F_n = list(filter(lambda x: x[1]%n == 0, E))
F_m = list(filter(lambda x: x[1]%m == 0, E))
L_n = [ l[0] for l in F_n]
L_m = [ J[0] for J in F_m]
return L_n + L_m
>>>Index(List, 2, 5):
[1, 3, 5]
Why that code doesn't returns [1, 3, 5, 4, 6]?
What is the mistake?
And how to create the function that returns that list?
You can use a list comprehension in combination with enumerate method.
Also, you can apply extended iterable unpacking operator in order to pass parameters as many you need.
List = [1, 2, 3, 4, 5, 6, 15]
def indexes(List, *vars):
return [index for index, item in enumerate(List) for i in vars if item % i == 0 ]
print(indexes(List, 2, 5))
Output
[1, 3, 5, 4, 6]
A more general and Pythonic approach that works for any number of variables is to use an any() or all() function that check the Truth value of the condition for all the arguments. If you want the index to belongs to an item that is divisible buy all the arguments you need all() other wise you can use any() that returns True right after it encounters a match.
def indexes(lst, *args):
return [i for i, j in enumerate(lst) if any(j % arg == 0 for arg in args)]
Demo:
>>> lst = [1, 2, 3, 4, 5, 6, 15, 99, 200, 13, 17, 400]
>>> indexes(lst, 99, 5, 2, 100)
[1, 3, 4, 5, 6, 7, 8, 11]
>>>
And with all():
>>> indexes(lst, 5, 2, 100)
[8, 11]
The issue is enumerate returns an iterator from an iterable. Once it is exhausted, you may not use it again. Therefore, you can simply define a new enumerate iterator:
lst = [1, 2, 3, 4, 5, 6, 15]
def Index( L, n, m):
# enumeration - notice we define 2 objects
E, F = enumerate(L), enumerate(L)
F_n = list(filter(lambda x: x[1]%n == 0, E))
F_m = list(filter(lambda x: x[1]%m == 0, F))
L_n = [ l[0] for l in F_n]
L_m = [ J[0] for J in F_m]
return L_n + L_m
res = Index(lst, 2, 5)
print(res)
[1, 3, 5, 4, 6]
Note there are better ways you can implement your algorithm.

Convert elements of list to a list of consecutive elements in an efficient way

I have a base list [1,4,10] which needs to be converted to a list having consecutive elements of each element in the base list in an efficient way
Examples:
If I need 2 consecutive numbers then [1,4,10] will be [1,2,4,5,10,11].
If 3 consecutive numbers then [1,4,10] will be [1,2,3,4,5,6,10,11,12].
arr=[1,4,10]
con=3
[r + i for r in arr for i in range(con)]
# [1, 2, 3, 4, 5, 6, 10, 11, 12]
Here's a one liner, assuming the list is x and the number of 'consecutives' is c:
reduce(lambda a, b: a + b, map(lambda x: range(x, x+c), x))
a = [1,4,10]
k = 3 #no of consecutive
x=[range(b,b+k) for b in a]
output = [m for d in x for m in d]
Here is one way. itertools.chain removes the need for explicit nested loops.
from itertools import chain
def consecutiver(lst, n=3):
return list(chain.from_iterable(range(i, i+n) for i in lst))
res = consecutiver([1, 4, 10], 2)
# [1, 2, 4, 5, 10, 11]
res2 = consecutiver([1, 4, 10], 3)
# [1, 2, 3, 4, 5, 6, 10, 11, 12]

Python 2.7 Mode of list without using built-in functions

On Python 2.7 I want to create a function which calculates the Mode of a list without using any built-in functions (e.g, not allowed to use Count)
Try the following function, which does not use sum(), count(), or any built-in function apart from append, to add to a list.
def mode(lst):
sums = {}
for i in lst:
if i in sums:
sums[i]+=1
else:
sums[i] = 1
maxModes = [[-1, -1]]
for j in sums:
if sums[j] > maxModes[0][0]:
maxModes = [[sums[j], j]]
elif sums[j] == maxModes[0][0]:
maxModes.append([sums[j], j])
indices = 0
for k in maxModes:
indices+=1
if indices == 1:
return maxModes[0][1]
else:
thisSum = 0
for l in maxModes:
thisSum+=l[1]
return float(thisSum)/indices
>>> mode([1, 2, 3, 3, 4])
3
>>> mode([1, 2, 3, 3, 4, 4])
3.5
>>> mode([1, 2, 3, 3, 4, 4, 5, 5, 5])
5
>>> mode([1, 2, 3, 3, 4, 4, 5, 5, 5, 6, 6, 6])
5.5
>>> mode([1, 2, 3, 3, 4, 4, 5, 5, 5, 6, 6, 6, 9, 9, 9])
6.666666666666667
>>> mode([1, 2])
1.5
>>> mode([1])
1
>>> mode([])
-1
>>>
Taking an example of k = [1,2,3,5,3,1,6,3] as a sample list whose mode needs to be calculated
def quickSrt(lst):
if len(lst) < 2:
return lst
pivot = lst[0]
l = quickSrt([x for x in lst[1:] if x < pivot])
r = quickSrt([x for x in lst[1:] if x >= pivot])
return l + [pivot] + r
k = [1,2,3,5,3,1,6,3]
d = {}
for i in k:
if i in d:
d[i] += 1
else:
d[i] = 1
values = [value for key, value in d.items()]
val = quickSrt(values)
for x,y in d.items():
if y == val[-1]:
print(x)

Categories

Resources