Using lambda function in generator expression - python

I am trying to count the total number of occurrences of a given val in the list using a lambda function:
def countOccurrence(givenList, val):
result = sum(1 for i in range(len(givenList)) if lambda i: givenList(i) == val)
return result
givenList = [3, 4, 5, 8, 0, 3, 8, 5, 0, 3, 1, 5, 2, 3, 4, 2]
print(countOccurrence(givenList, 5))
But the returned result is 16, which is nothing but the length of the list.

if you are trying to count the number of 5's in a list you should use the builtin
my_list.count(5)

Why use a lambda?
def countOccurrence(givenList, val):
result = sum(1 for i in range(len(givenList)) if givenList[i] == val)
return result
givenList = [3, 4, 5, 8, 0, 3, 8, 5, 0, 3, 1, 5, 2, 3, 4, 2]
print(countOccurrence(givenList, 5))

If you are trying to count the number of 5's in a list you should use the Counter. You get the numbers of all other elements as a bonus:
from collections import Counter
cntr = Counter(givenList)
#Counter({3: 4, 5: 3, 4: 2, 8: 2, 0: 2, 2: 2, 1: 1})
cntr[5]
# 3

As is often the case in Python, the best way to do something its make use of its built-ins as much as possible because they've frequently been written in C. In this case that would be the count() method of sequence objects, my_list.count(5) as #Joran Beasley suggests in his answer.
Regardless, for future reference the code below shows how to use a lambda function in a generator expression like you where trying to do. Note that the lambda function needs to be defined outside of the expression itself and also what its definition needs to (because what you had wasn't quite correct).
def countOccurrence(givenList, val):
check_list = lambda i: givenList[i] == val
return sum(1 for i in range(len(givenList)) if check_list(i))
givenList = [3, 4, 5, 8, 0, 3, 8, 5, 0, 3, 1, 5, 2, 3, 4, 2]
print(countOccurrence(givenList, 5)) # -> 3

Related

Finding the indexes of a list

I have the following series and I am supposed to pinpoint with a loop the indices that contain exactly the value 6:
x=[1, 3, 2, 1, 1, 6, 4, 2]
results=[]
Upon making my code, however, I am getting the output none. What could be going wrong?
def throwing_6(x):
for index,throw in enumerate(x):
if throw==6:
results.append(index)
results
indexes = throwing_6([1, 2, 6, 3, 6, 1, 2, 6])
print(indexes)
You forgot to return the results array at the end of the function.
def throwing_6(x):
for index,throw in enumerate(x):
if throw==6:
results.append(index)
return results;
indexes = throwing_6([1, 2, 6, 3, 6, 1, 2, 6])
print(indexes)
You could also use a simple list comprehension with condition as follows:
def throwing_6(x):
return [i for i, v in enumerate(x) if v == 6]
Upon reading user feedback I found the solution see the code between two stars.
def throwing_6(x):
for index,throw in enumerate(x):
if throw==6:
results.append(index)
**return results**
indexes = throwing_6([1, 2, 6, 3, 6, 1, 2, 6])
print(indexes)

Python move all elements of a list one position back with same len

def list_move_back(new_value, value_list):
for i in reversed(value_list):
if value_list.index(i) != len(value_list)-1:
value_list[value_list.index(i)+1] = i
value_list[0] = new_value
return value_list
I want to get the following result:
list_example = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
list_example = list_move_back(12, list_example]
print(list_example)
>>>[12, 1, 2, 3, 4, 5, 6, 7, 8, 9]
It works if I run the function two times:
list_example = list_move_back(12, list_example]
print(list_example)
>>>[12, 12, 1, 2, 3, 4, 5, 6, 7, 8]
but if I want to run it a third time, the result looks like that:
list_example = list_move_back(12, list_example]
print(list_example)
>>>[12, 12, 1, 1, 3, 4, 5, 6, 7, 8]
The first 1 should be a 12. I have no idea why it doesn't work.
Just use list slicing:
def list_move_back(new_value, list_of_values):
return [new_value] + list_of_values[:-1]
Explanation: list_of_values[:-1] returns all the elements except for the last. By appending it to the new value, you get the wanted result. This answer has a pretty cool explanation of how list slicing works.
Also, if for some reason you'd like the "verbose" way to do this (maybe for an exercise or whatever), here's a way to go about it:
def list_move_back(new_value, list_of_values):
for i in range(len(list_of_values)-1, 0, -1):
list_of_values[i] = list_of_values[i-1]
list_of_values[0] = new_value
return list_of_values
I'd recommend list slicing over this method 9/10 times but again, I'm just leaving this here because there might be a case where someone wants to do this as some sort of mental exercise for indexing.
If you need the list to change in place, you can use the list methods .pop() to remove the last item and .insert(0,value) to add an item to the front:
>>> L = list(range(1,11))
>>> L
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> id(L)
1772071032392
>>> L.pop();L.insert(0,12)
10
>>> L
[12, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> id(L) # same list id, modified in place...
1772071032392

Is it possible to sort a list with reduce?

I was given this as an exercise. I could of course sort a list by using sorted() or other ways from Python Standard Library, but I can't in this case. I think I'm only supposed to use reduce().
from functools import reduce
arr = [17, 2, 3, 6, 1, 3, 1, 9, 5, 3]
sorted_arr = reduce(lambda a,b : (b,a) if a > b else (a,b), arr)
The error I get:
TypeError: '>' not supported between instances of 'tuple' and 'int'
Which is expected, because my reduce function inserts a tuple into the int array, instead of 2 separate integers. And then the tuple gets compared to an int...
Is there a way to insert back 2 numbers into the list, and only run the function on every second number in the list? Or a way to swap the numbers with using reduce()?
Documentation says very little about the reduce function, so I am out of ideas right now.
https://docs.python.org/3/library/functools.html?highlight=reduce#functools.reduce
Here is one way to sort the list using reduce:
arr = [17, 2, 3, 6, 1, 3, 1, 9, 5, 3]
sorted_arr = reduce(
lambda a, b: [x for x in a if x <= b] + [b] + [x for x in a if x > b],
arr,
[]
)
print(sorted_arr)
#[1, 1, 2, 3, 3, 3, 5, 6, 9, 17]
At each reduce step, build a new output list which concatenates a list of all of the values less than or equal to b, [b], and a list of all of the values greater than b. Use the optional third argument to reduce to initialize the output to an empty list.
I think you're misunderstanding how reduce works here. Reduce is synonymous to right-fold in some other languages (e.g. Haskell). The first argument expects a function which takes two parameters: an accumulator and an element to accumulate.
Let's hack into it:
arr = [17, 2, 3, 6, 1, 3, 1, 9, 5, 3]
reduce(lambda xs, x: [print(xs, x), xs+[x]][1], arr, [])
Here, xs is the accumulator and x is the element to accumulate. Don't worry too much about [print(xs, x), xs+[x]][1] – it's just there to print intermediate values of xs and x. Without the printing, we could simplify the lambda to lambda xs, x: xs + [x], which just appends to the list.
The above outputs:
[] 17
[17] 2
[17, 2] 3
[17, 2, 3] 6
[17, 2, 3, 6] 1
[17, 2, 3, 6, 1] 3
[17, 2, 3, 6, 1, 3] 1
[17, 2, 3, 6, 1, 3, 1] 9
[17, 2, 3, 6, 1, 3, 1, 9] 5
[17, 2, 3, 6, 1, 3, 1, 9, 5] 3
As we can see, reduce passes an accumulated list as the first argument and a new element as the second argument.(If reduce is still boggling you, How does reduce work? contains some nice explanations.)
Our particular lambda inserts a new element into the accumulator on each "iteration". This hints at insertion sort:
def insert(xs, n):
"""
Finds first element in `xs` greater than `n` and returns an inserted element.
`xs` is assumed to be a sorted list.
"""
for i, x in enumerate(xs):
if x > n:
return xs[:i] + [n] + xs[i:]
return xs + [n]
sorted_arr = reduce(insert, arr, [])
print(sorted_arr)
This prints the correctly sorted array:
[1, 1, 2, 3, 3, 3, 5, 6, 9, 17]
Note that a third parameter to reduce (i.e. []) was specified as we initialise the sort should with an empty list.
Ninjad! But yes, it's an insertion sort.
def insert(acc, e):
for i, x in enumerate(acc):
if x > e:
acc.insert(i, e)
return acc
acc.append(e)
return acc
reduce(insert, [1, 2, 6, 4, 7, 3, 0, -1], [])
outputs
[-1, 0, 1, 2, 3, 4, 6, 7]
After some thinking I concluded that it is also possible to do swap-based sort, if you are allowed to use reduce more than once. Namely:
from functools import reduce
arr = [17, 2, 3, 6, 1, 3, 1, 9, 5, 3]
def func(acc,x):
if not acc:
return [x]
if acc[-1]<x:
return acc+[x]
else:
return acc[:-1]+[x]+acc[-1:]
def my_sort(x):
moresorted = reduce(func,x,[])
print(moresorted)
if x==moresorted:
return moresorted
else:
return my_sort(moresorted)
print('arr:',arr)
arr_sorted = my_sort(arr)
print('arr sorted:',arr_sorted)
Output:
arr: [17, 2, 3, 6, 1, 3, 1, 9, 5, 3]
[2, 3, 6, 1, 3, 1, 9, 5, 3, 17]
[2, 3, 1, 3, 1, 6, 5, 3, 9, 17]
[2, 1, 3, 1, 3, 5, 3, 6, 9, 17]
[1, 2, 1, 3, 3, 3, 5, 6, 9, 17]
[1, 1, 2, 3, 3, 3, 5, 6, 9, 17]
[1, 1, 2, 3, 3, 3, 5, 6, 9, 17]
arr sorted: [1, 1, 2, 3, 3, 3, 5, 6, 9, 17]
I placed print(moresorted) inside func for educational purposes, you could remove it if you wish.
Now explanation: my_sort is recursive function, with every run of it list become more and more sorted. func which is used as function in reduce does append new element and then swaps 2 last elements of list if they are not in ascending order.
This mean in every run of my_sort number "travels" rightward until in take place where next number is bigger.
if not acc is required for starting - notice that third argument of reduce is [] meaning that during first execution of func in each reduce first argument for func is [], so asking acc[-1]<x? would result in error.
Let's understand this
(1)Usage of Reduce is basically to reduce the expression to a single final value
(2)reduce() stores the intermediate result and only returns the final summation value
(3)We will take the smallest element using reduce, append it to sorted_list and remove from the original list
(4)Now the reduce will work on the rest of the elements and repeat step 3 again
(5)while list_nums: will run until the list becomes empty
list_of_nums = [1,19,5,17,9]
sorted_list=[]
while list_of_nums:
maxvalue=reduce(lambda x,y: x if x<y else y,list_of_nums)
sorted_list.append(maxvalue)
list_of_nums.remove(maxvalue)
print(sorted_list)
[1, 5, 9, 17, 19]

Repeat list if index range is out of bounds

I have a Python list
a = [1, 2, 3, 4]
and I'd like to get a range of indices such that if I select the indices 0 through N, I'm getting (for N=10) the repeated
[1, 2, 3, 4, 1, 2, 3, 4, 1, 2]
I could of course repeat the list via (int(float(N) / len(a) - 0.5) + 1) * a first and select the range [0:10] out of that, but that feels rather clumsy.
Any hints?
You can simply use the modulo operator when accessing the list, i.e.
a[i % len(a)]
This will give you the same result, but doesn't require to actually store the redundant elements.
You can use itertools.cycle and itertools.islice:
from itertools import cycle, islice
my_list = list(islice(cycle(my_list), 10))
Note that if you just want to iterate over this once, you should avoid calling list and just iterate over the iterable, since this avoids allocating repeated elements.
One easy way is to use modulo with list comprehensions à la
a = [1, 2, 3 ,4]
[k % len(a) for k in range(10)]
>>> a = [1, 2, 3, 4]
>>> (a*3)[:-2]
>>> [1, 2, 3, 4, 1, 2, 3, 4, 1, 2]
Thought I would offer a solution using the * operator for lists.
import math
def repeat_iterable(a, N):
factor = N / len(a) + 1
repeated_list = a * factor
return repeated_list[:N]
Sample Output:
>>> print repeat_iterable([1, 2, 3, 4], 10)
[1, 2, 3, 4, 1, 2, 3, 4, 1, 2]
>>> print repeat_iterable([1, 2, 3, 4], 3)
[1, 2, 3]
>>> print repeat_iterable([1, 2, 3, 4], 0)
[]
>>> print repeat_iterable([1, 2, 3, 4], 14)
[1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2]
How about faking it? Python is good at faking.
class InfiniteList(object):
def __init__(self, data):
self.data = data
def __getitem__(self, i):
return self.data[i % len(self.data)]
x = InfiniteList([10, 20, 30])
x[0] # 10
x[34] # 20
Of course, you could add __iter__, support for slices etc. You could also add a limit (N), but this is the general idea.

Sort list by frequency

Is there any way in Python, wherein I can sort a list by its frequency?
For example,
[1,2,3,4,3,3,3,6,7,1,1,9,3,2]
the above list would be sorted in the order of the frequency of its values to create the following list, where the item with the greatest frequency is placed at the front:
[3,3,3,3,3,1,1,1,2,2,4,6,7,9]
I think this would be a good job for a collections.Counter:
counts = collections.Counter(lst)
new_list = sorted(lst, key=lambda x: -counts[x])
Alternatively, you could write the second line without a lambda:
counts = collections.Counter(lst)
new_list = sorted(lst, key=counts.get, reverse=True)
If you have multiple elements with the same frequency and you care that those remain grouped, we can do that by changing our sort key to include not only the counts, but also the value:
counts = collections.Counter(lst)
new_list = sorted(lst, key=lambda x: (counts[x], x), reverse=True)
l = [1,2,3,4,3,3,3,6,7,1,1,9,3,2]
print sorted(l,key=l.count,reverse=True)
[3, 3, 3, 3, 3, 1, 1, 1, 2, 2, 4, 6, 7, 9]
You can use a Counter to get the count of each item, use its most_common method to get it in sorted order, then use a list comprehension to expand again
>>> lst = [1,2,3,4,3,3,3,6,7,1,1,9,3,2]
>>>
>>> from collections import Counter
>>> [n for n,count in Counter(lst).most_common() for i in range(count)]
[3, 3, 3, 3, 3, 1, 1, 1, 2, 2, 4, 6, 7, 9]
In case you want to use a double comparator.
For example: Sort the list by frequency in descending order and in case of a clash the smaller one comes first.
import collections
def frequency_sort(a):
f = collections.Counter(a)
a.sort(key = lambda x:(-f[x], x))
return a
Was practising this one for fun. This solution use less time complexity.
from collections import defaultdict
lis = [1,2,3,4,3,3,3,6,7,1,1,9,3,2]
dic = defaultdict(int)
for num in lis:
dic[num] += 1
s_list = sorted(dic, key=dic.__getitem__, reverse=True)
new_list = []
for num in s_list:
for rep in range(dic[num]):
new_list.append(num)
print(new_list)
def orderByFrequency(list):
listUniqueValues = np.unique(list)
listQty = []
listOrderedByFrequency = []
for i in range(len(listUniqueValues)):
listQty.append(list.count(listUniqueValues[i]))
for i in range(len(listQty)):
index_bigger = np.argmax(listQty)
for j in range(listQty[index_bigger]):
listOrderedByFrequency.append(listUniqueValues[index_bigger])
listQty[index_bigger] = -1
return listOrderedByFrequency
#tests:
print(orderByFrequency([1,2,3,4,3,3,3,6,7,1,1,9,3,2]))
print(orderByFrequency([1,2,2]))
print(orderByFrequency([1,2,1,2]))
print(orderByFrequency([2,1,2,1]))
print(orderByFrequency([3,3,3,4,4,4,4,1,5,5,5,5,5,2,2]))
print(orderByFrequency([3,3,3,6,6,6,4,4,4,4,1,6,6,5,5,5,5,5,2,2]))
print(orderByFrequency([10,20,30,30,30,40,40,50,50,50]))
results:
[3, 3, 3, 3, 3, 1, 1, 1, 2, 2, 4, 6, 7, 9]
[2, 2, 1]
[1, 1, 2, 2]
[1, 1, 2, 2]
[5, 5, 5, 5, 5, 4, 4, 4, 4, 3, 3, 3, 2, 2, 1]
[5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 4, 4, 4, 4, 3, 3, 3, 2, 2, 1]
[30, 30, 30, 50, 50, 50, 40, 40, 10, 20]
from collections import Counter
a = [2, 5, 2, 6, -1, 9999999, 5, 8, 8, 8]
count = Counter(a)
a = []
while len(count) > 0:
c = count.most_common(1)
for i in range(c[0][1]):
a.append(c[0][0])
del count[c[0][0]]
print(a)
You can use below methods. It is written in simple python.
def frequencyIdentification(numArray):
frequency = dict({});
for i in numArray:
if i in frequency.keys():
frequency[i]=frequency[i]+1;
else:
frequency[i]=1;
return frequency;
def sortArrayBasedOnFrequency(numArray):
sortedNumArray = []
frequency = frequencyIdentification(numArray);
frequencyOrder = sorted(frequency, key=frequency.get);
loop = 0;
while len(frequencyOrder) > 0:
num = frequencyOrder.pop()
count = frequency[num];
loop = loop+1;
while count>0:
loop = loop+1;
sortedNumArray.append(num);
count=count-1;
print("loop count");
print(loop);
return sortedNumArray;
a=[1, 2, 3, 4, 3, 3, 3, 6, 7, 1, 1, 9, 3, 2]
print(a);
print("sorted array based on frequency of the number");
print(sortArrayBasedOnFrequency(a));

Categories