Finding only pairs in list using list comprehension - python

Looking for a fancy one line solution to finding pairs of items in a list using list comprehension.
I've got some code that finds multiples, but can't figure out how to split those multiples into pairs.
lst = [1,2,4,2,2,3,3,1,1,1,2,4,3,4,1]
len(set([x for x in lst if lst.count(x) > 1]))
Code above returns 4. Answer should be 6 pairs, [1,1,1,1,1] = 2, [2,2,2,2] = 2, [3,3,3] = 1 and [4,4,4] = 1.

Another approach would be using [Python 3.Docs]: class collections.Counter([iterable-or-mapping]):
>>> from collections import Counter
>>>
>>> lst = [1, 2, 4, 2, 2, 3, 3, 1, 1, 1, 2, 4, 3, 4, 1]
>>>
>>> c = Counter(lst)
>>> c
Counter({1: 5, 2: 4, 4: 3, 3: 3})
>>>
>>> sum(item // 2 for item in c.values())
6
and the one line equivalent:
>>> sum(item // 2 for item in Counter(lst).values())
6

A one-liner with no other intermediate variables would be:
sum(lst.count(x)//2 for x in set(lst))
It loops over set(lst) which contains all the distinct numbers in lst, and adds their pair counts.

You can do the following (if I've understood your pairing method properly):
lst = [1,2,4,2,2,3,3,1,1,1,2,4,3,4,1]
the_dict = {x: int((lst.count(x)/2)) for x in lst}
print(sum(the_dict.values()))
> 6
print(the_dict)
> {1: 2, 2: 2, 4: 1, 3: 1}
This makes a dictionary with the count of all pairs, then you can sum the values in the dictionary to get the pair count. Then you also have the dictionary available with the pair count of each value, if you need it.

Related

Get common elements majority of lists in python

Given 4 lists, I want to get elements that are common to 3 or more lists.
a = [1, 2, 3, 4]
b = [1, 2, 3, 4, 5]
c = [1, 3, 4, 5, 6]
d = [1, 2, 6, 7]
Hence, the output should be [1, 2, 3, 4].
My current code is as follows.
result1 = set(a) & set(b) & set(c)
result2 = set(b) & set(c) & set(d)
result3 = set(c) & set(d) & set(a)
result4 = set(d) & set(a) & set(b)
final_result = list(result1)+list(result2)+list(result3)+list(result4)
print(set(final_result))
It works fine, and give the desired output. However, I am interested in knowing if there is an easy way of doing this in Python, ie: are there any built in functions for this?
Using a Counter, you can do this like:
Code:
a = [1, 2, 3, 4]
b = [1, 2, 3, 4, 5]
c = [1, 3, 4, 5, 6]
d = [1, 2, 6, 7]
from collections import Counter
counts = Counter(sum(([list(set(i)) for i in (a, b, c, d)]), []))
print(counts)
more_than_three = [i for i, c in counts.items() if c >= 3]
print(more_than_three)
Results:
Counter({1: 4, 2: 3, 3: 3, 4: 3, 5: 2, 6: 2, 7: 1})
[1, 2, 3, 4]
Iterate over the values in all lists to create a dict of {value: number_of_lists_the_value_appears_in}:
from collections import defaultdict
counts = defaultdict(int)
for list_ in (a, b, c, d):
for value in set(list_): # eliminate duplicate values with `set`
counts[value] += 1
Then in the second step remove all values with a count < 3:
result = [value for value, count in counts.items() if count >= 3]
print(result) # [1, 2, 3, 4]
The code below will solve the generalised problem (with n lists, and a requirement that a common element must be in at least k of them). It will work with non-hashable items, which is the main disadvantage of all the other answers:
a = [1, 2, 3, 4]
b = [1, 2, 3, 4, 5]
c = [1, 2, 3, 4, 4, 5, 6]
d = [1, 2, 6, 7]
lists = [a, b, c, d]
result = []
desired_quanity = 3
for i in range(len(lists) - desired_quanity + 1): #see point 1 below
sublist = lists.pop(0) #see point 2
for item in sublist:
counter = 1 #1 not 0, by virute of the fact it is in sublist
for comparisonlist in lists:
if item in comparisonlist:
counter += 1
comparisonlist.remove(item) #see point 3
if counter >= desired_quanity:
result.append(item)
This has the disadvantage that for each element in every list, we have to check in every other list to see if it is there, but we can make things more efficient in a few ways. Also look-ups are alot slower in lists than sets (which we can't use since the OP has non-hashable items in the lists), and so this may be slow for very large lists.
1) If we require an item to be in k lists, we don't need to check each item in the last k-1 lists, as we would have already picked it up whilst searching through the first k lists.
2) Once we have searched through a list, we can discard that list, since any items in the just-searched-list that might contribute to our final result, will again already have been dealt with. This means that with each iteration we have fewer lists to search through.
3) When we have checked if an item is in enough lists, we can remove that item from the list, which means not only is the number of lists getting shorter as we proceed, the lists themselves are getting shorter, meaning quicker lookups.
As an aftersort, if we the original lists happen to be sorted beforehand, this might also help this algorithm work efficiently.
create a dictionary of counts and filter out those with count less than 3

Iterating Over List, Combining Quantities

I have created a simple program to take a list of simplified sku's and qty's and add it to another list. However if the sku is already in the second list I would like to just increment the qty as opposed to adding a copy. I have tried a few different for loops and I haven't been able to get it to work.
myList = [["a",1],["c",1],["a",1]] #[sku, qty]
newList = [["null",0]] #placeholder value so second for loop functions
for eachItem in myList:
for eachNew in newList:
if eachItem[0] == eachNew[0]: # if sku is in list increment qty
eachNew[1] += eachItem[1]
myList.remove(eachItem)
else:
newList.append([eachItem[0], eachItem[1]]) #else add the sku to the list
myList.remove(eachItem)
#remove null place holder
for eachItem in newList:
if eachItem[0] == "null":
newList.remove(eachItem)
for eachItem in newList:
print(eachItem)
My desired output would be:
['a', 2]
['c', 1]
EDIT: I just realized I wasn't clear enough in my OP. I don't want to count the number of times a sku appears I want to add all the quantities. It is possible that there will be quantities of more than one.
Try using value_counts from the pandas libray like so:
import pandas as pd
myList = [["a",1],["c",1],["a",1]]
myList = pd.Series(myList)
mylist.value_counts().to_list()
would yeild:
[['a',2],['c',1]]
like this example
In [83]: data
array([4, 6, 6, 1, 2, 1, 0, 5, 3, 2, 4, 3, 1, 3, 5, 3, 0, 0, 4, 4, 6, 1, 0,
4, 3, 2, 1, 3, 1, 5, 6, 3, 1, 2, 4, 4, 3, 3, 2, 2, 2, 3, 2, 3, 0, 1,
2, 4, 5, 5])
In [84]: s = Series(data)
In [85]: s.value_counts()
3 11
2 9
4 8
1 8
5 5
0 5
6 4
dtype: int64
The programming behind it is much more efficient than a for loop because the basis is written in C. You can use the to_list() method, mylist.value_counts().to_list(), to get it exactly to your desired output.
* this code is untested
Look into the the collections.Counter class in the python library documentation. It won't require any loops.
How about something like this?
This is a dict, that maps a sku to a quantity, that will allow you to aggregate values together and keep track of what you have already counted
Dont over complicate it
myList = [["a",1],["c",1],["a",1]] #[sku, qty]
counter_dict = {} #schema {"sku": quantity:int }
for eachItem in myList:
if eachItem[0] in counter_dict:
counter_dict[eachItem[0]] += eachItem[1]
else:
counter_dict[eachItem[0]] = eachItem[1]
for key in counter_dict.keys():
print([key, counter_dict[key]])
Not as elegant as pandas and I couldn't figure out collections.Counter so, this is more along the lines of what you were doing. It builds a dictionary and then builds the list again with a comprehension.
mylist = [["a",1],["c",1],["a",1]]
newlist = [["null",0]]
mydict = {}
for i in mylist+newlist:
if i[0] in mydict:
mydict[i[0]]+=i[1]
elif i[0] != 'null':
mydict[i[0]]=i[1]
print [[x,mydict[x]] for x in mydict]
[['a', 2], ['c', 1]]

How to compare values within an array in Python - find out whether 2 values are the same

I basically have an array of 50 integers, and I need to find out whether any of the 50 integers are equal, and if they are, I need to carry out an action.
How would I go about doing this? As far as I know there isn't currently a function in Python that does this is there?
If you mean you have a list and you want to know if there are any duplicate values, then make a set from the list and see if it's shorter than the list:
if len(set(my_list)) < len(my_list):
print "There's a dupe!"
This won't tell you what the duplicate value is, though.
If you have Python 2.7+ you can use Counter.
>>> import collections
>>> input = [1, 1, 3, 6, 4, 8, 8, 5, 6]
>>> c = collections.Counter(input)
>>> c
Counter({1: 2, 6: 2, 8: 2, 3: 1, 4: 1, 5: 1})
>>> duplicates = [i for i in c if c[i] > 1]
>>> duplicates
[1, 6, 8]
If your actions need to know the number or how many times that number gets repeated over your input list then groupby is a good choice.
>>> from itertools import groupby
>>> for x in groupby([1,1,2,2,2,3]):
... print x[0],len(list(x[1]))
...
1 2
2 3
3 1
The first number is the element and the second the number of repetitions. groupby works over a sorted list so make sure you sort your input list, for instance.
>>> for x in groupby(sorted([1,1,2,4,2,2,3])):
... print x[0],len(list(x[1]))
...
1 2
2 3
3 1
4 1
You can convert the list to a Set, and check their lengths.
>>> a = [1, 2, 3]
>>> len(set(a)) == len(a)
True
>>> a = [1, 2, 3, 4, 4]
>>> len(set(a)) == len(a)
False
>>> arbitrary_list = [1, 1, 2, 3, 4, 4, 4]
>>> item_occurence = dict([(item, list.count(item)) for item in arbitrary_list])
{1: 2, 2: 1, 3: 1, 4: 3}
If you want to see which values are not unique you can get a list of those values by
>>> filter(lambda item: item_occurence[item] > 1, item_occurence)
[1, 4]

Python dictionary sum values

I'm using Python 2.7 and I have a large dictionary that looks a little like this
{J: [92704, 238476902378, 32490872394, 234798327, 2390470], M: [32974097, 237407, 3248707, 32847987, 34879], Z: [8237, 328947, 239487, 234, 182673]}
How can I sum these by value to create a new dictionary that sums the first values in each dictionary, then the second, etc. Like
{FirstValues: J[0]+M[0]+Z[0]}
etc
In [4]: {'FirstValues': sum(e[0] for e in d.itervalues())}
Out[4]: {'FirstValues': 33075038}
where d is your dictionary.
print [sum(row) for row in zip(*yourdict.values())]
yourdict.values() gets all the lists, zip(* ) groups the first, second, etc items together and sum sums each group.
I don't know why do you need dictionary as output, but here it is:
dict(enumerate( [sum(x) for x in zip(*d.values())] ))
from itertools import izip_longest
totals = (sum(vals) for vals in izip_longest(*mydict.itervalues(), fillvalue=0))
print tuple(totals)
In English...
zip the lists (dict values) together, padding with 0 (if you want, you don't have to).
Sum each zipped group
For example,
mydict = {
'J': [1, 2, 3, 4, 5],
'M': [1, 2, 3, 4, 5],
'Z': [1, 2, 3, 4]
}
## When zipped becomes...
([1, 1, 1], [2, 2, 2], [3, 3, 3], [4, 4, 4], [5, 5, 0])
## When summed becomes...
(3, 6, 9, 12, 10)
It does really not make sense to create a new dictionary as the new keys are (probably) meaningless. The results don't relate to the original keys. More appropriate is a tuple as results[0] holds the sum of all values at position 0 in the original dict values etc.
If you must have a dict, take the totals iterator and turn it into a dict thus:
new_dict = dict(('Values%d' % idx, val) for idx, val in enumerate(totals))
Say you have some dict like:
d = {'J': [92704, 238476902378, 32490872394, 234798327, 2390470],
'M': [32974097, 237407, 3248707, 32847987, 34879],
'Z': [8237, 328947, 239487, 234, 182673]}
Make a defaultdict (int)
from collections import defaultdict
sum_by_index = defaultdict(int)
for alist in d.values():
for index,num in enumerate(alist):
sum_by_index[index] += num

How to count the frequency of the elements in an unordered list? [duplicate]

This question already has answers here:
Using a dictionary to count the items in a list
(8 answers)
Closed 7 months ago.
Given an unordered list of values like
a = [5, 1, 2, 2, 4, 3, 1, 2, 3, 1, 1, 5, 2]
How can I get the frequency of each value that appears in the list, like so?
# `a` has 4 instances of `1`, 4 of `2`, 2 of `3`, 1 of `4,` 2 of `5`
b = [4, 4, 2, 1, 2] # expected output
In Python 2.7 (or newer), you can use collections.Counter:
>>> import collections
>>> a = [5, 1, 2, 2, 4, 3, 1, 2, 3, 1, 1, 5, 2]
>>> counter = collections.Counter(a)
>>> counter
Counter({1: 4, 2: 4, 5: 2, 3: 2, 4: 1})
>>> counter.values()
dict_values([2, 4, 4, 1, 2])
>>> counter.keys()
dict_keys([5, 1, 2, 4, 3])
>>> counter.most_common(3)
[(1, 4), (2, 4), (5, 2)]
>>> dict(counter)
{5: 2, 1: 4, 2: 4, 4: 1, 3: 2}
>>> # Get the counts in order matching the original specification,
>>> # by iterating over keys in sorted order
>>> [counter[x] for x in sorted(counter.keys())]
[4, 4, 2, 1, 2]
If you are using Python 2.6 or older, you can download an implementation here.
If the list is sorted, you can use groupby from the itertools standard library (if it isn't, you can just sort it first, although this takes O(n lg n) time):
from itertools import groupby
a = [5, 1, 2, 2, 4, 3, 1, 2, 3, 1, 1, 5, 2]
[len(list(group)) for key, group in groupby(sorted(a))]
Output:
[4, 4, 2, 1, 2]
Python 2.7+ introduces Dictionary Comprehension. Building the dictionary from the list will get you the count as well as get rid of duplicates.
>>> a = [1,1,1,1,2,2,2,2,3,3,4,5,5]
>>> d = {x:a.count(x) for x in a}
>>> d
{1: 4, 2: 4, 3: 2, 4: 1, 5: 2}
>>> a, b = d.keys(), d.values()
>>> a
[1, 2, 3, 4, 5]
>>> b
[4, 4, 2, 1, 2]
Count the number of appearances manually by iterating through the list and counting them up, using a collections.defaultdict to track what has been seen so far:
from collections import defaultdict
appearances = defaultdict(int)
for curr in a:
appearances[curr] += 1
In Python 2.7+, you could use collections.Counter to count items
>>> a = [1,1,1,1,2,2,2,2,3,3,4,5,5]
>>>
>>> from collections import Counter
>>> c=Counter(a)
>>>
>>> c.values()
[4, 4, 2, 1, 2]
>>>
>>> c.keys()
[1, 2, 3, 4, 5]
Counting the frequency of elements is probably best done with a dictionary:
b = {}
for item in a:
b[item] = b.get(item, 0) + 1
To remove the duplicates, use a set:
a = list(set(a))
You can do this:
import numpy as np
a = [1,1,1,1,2,2,2,2,3,3,4,5,5]
np.unique(a, return_counts=True)
Output:
(array([1, 2, 3, 4, 5]), array([4, 4, 2, 1, 2], dtype=int64))
The first array is values, and the second array is the number of elements with these values.
So If you want to get just array with the numbers you should use this:
np.unique(a, return_counts=True)[1]
Here's another succint alternative using itertools.groupby which also works for unordered input:
from itertools import groupby
items = [5, 1, 1, 2, 2, 1, 1, 2, 2, 3, 4, 3, 5]
results = {value: len(list(freq)) for value, freq in groupby(sorted(items))}
results
format: {value: num_of_occurencies}
{1: 4, 2: 4, 3: 2, 4: 1, 5: 2}
I would simply use scipy.stats.itemfreq in the following manner:
from scipy.stats import itemfreq
a = [1,1,1,1,2,2,2,2,3,3,4,5,5]
freq = itemfreq(a)
a = freq[:,0]
b = freq[:,1]
you may check the documentation here: http://docs.scipy.org/doc/scipy-0.16.0/reference/generated/scipy.stats.itemfreq.html
from collections import Counter
a=["E","D","C","G","B","A","B","F","D","D","C","A","G","A","C","B","F","C","B"]
counter=Counter(a)
kk=[list(counter.keys()),list(counter.values())]
pd.DataFrame(np.array(kk).T, columns=['Letter','Count'])
seta = set(a)
b = [a.count(el) for el in seta]
a = list(seta) #Only if you really want it.
Suppose we have a list:
fruits = ['banana', 'banana', 'apple', 'banana']
We can find out how many of each fruit we have in the list like so:
import numpy as np
(unique, counts) = np.unique(fruits, return_counts=True)
{x:y for x,y in zip(unique, counts)}
Result:
{'banana': 3, 'apple': 1}
This answer is more explicit
a = [1,1,1,1,2,2,2,2,3,3,3,4,4]
d = {}
for item in a:
if item in d:
d[item] = d.get(item)+1
else:
d[item] = 1
for k,v in d.items():
print(str(k)+':'+str(v))
# output
#1:4
#2:4
#3:3
#4:2
#remove dups
d = set(a)
print(d)
#{1, 2, 3, 4}
For your first question, iterate the list and use a dictionary to keep track of an elements existsence.
For your second question, just use the set operator.
def frequencyDistribution(data):
return {i: data.count(i) for i in data}
print frequencyDistribution([1,2,3,4])
...
{1: 1, 2: 1, 3: 1, 4: 1} # originalNumber: count
I am quite late, but this will also work, and will help others:
a = [1,1,1,1,2,2,2,2,3,3,4,5,5]
freq_list = []
a_l = list(set(a))
for x in a_l:
freq_list.append(a.count(x))
print 'Freq',freq_list
print 'number',a_l
will produce this..
Freq [4, 4, 2, 1, 2]
number[1, 2, 3, 4, 5]
a = [1,1,1,1,2,2,2,2,3,3,4,5,5]
counts = dict.fromkeys(a, 0)
for el in a: counts[el] += 1
print(counts)
# {1: 4, 2: 4, 3: 2, 4: 1, 5: 2}
a = [1,1,1,1,2,2,2,2,3,3,4,5,5]
# 1. Get counts and store in another list
output = []
for i in set(a):
output.append(a.count(i))
print(output)
# 2. Remove duplicates using set constructor
a = list(set(a))
print(a)
Set collection does not allow duplicates, passing a list to the set() constructor will give an iterable of totally unique objects. count() function returns an integer count when an object that is in a list is passed. With that the unique objects are counted and each count value is stored by appending to an empty list output
list() constructor is used to convert the set(a) into list and referred by the same variable a
Output
D:\MLrec\venv\Scripts\python.exe D:/MLrec/listgroup.py
[4, 4, 2, 1, 2]
[1, 2, 3, 4, 5]
Simple solution using a dictionary.
def frequency(l):
d = {}
for i in l:
if i in d.keys():
d[i] += 1
else:
d[i] = 1
for k, v in d.iteritems():
if v ==max (d.values()):
return k,d.keys()
print(frequency([10,10,10,10,20,20,20,20,40,40,50,50,30]))
#!usr/bin/python
def frq(words):
freq = {}
for w in words:
if w in freq:
freq[w] = freq.get(w)+1
else:
freq[w] =1
return freq
fp = open("poem","r")
list = fp.read()
fp.close()
input = list.split()
print input
d = frq(input)
print "frequency of input\n: "
print d
fp1 = open("output.txt","w+")
for k,v in d.items():
fp1.write(str(k)+':'+str(v)+"\n")
fp1.close()
from collections import OrderedDict
a = [1,1,1,1,2,2,2,2,3,3,4,5,5]
def get_count(lists):
dictionary = OrderedDict()
for val in lists:
dictionary.setdefault(val,[]).append(1)
return [sum(val) for val in dictionary.values()]
print(get_count(a))
>>>[4, 4, 2, 1, 2]
To remove duplicates and Maintain order:
list(dict.fromkeys(get_count(a)))
>>>[4, 2, 1]
i'm using Counter to generate a freq. dict from text file words in 1 line of code
def _fileIndex(fh):
''' create a dict using Counter of a
flat list of words (re.findall(re.compile(r"[a-zA-Z]+"), lines)) in (lines in file->for lines in fh)
'''
return Counter(
[wrd.lower() for wrdList in
[words for words in
[re.findall(re.compile(r'[a-zA-Z]+'), lines) for lines in fh]]
for wrd in wrdList])
For the record, a functional answer:
>>> L = [1,1,1,1,2,2,2,2,3,3,4,5,5]
>>> import functools
>>> >>> functools.reduce(lambda acc, e: [v+(i==e) for i, v in enumerate(acc,1)] if e<=len(acc) else acc+[0 for _ in range(e-len(acc)-1)]+[1], L, [])
[4, 4, 2, 1, 2]
It's cleaner if you count zeroes too:
>>> functools.reduce(lambda acc, e: [v+(i==e) for i, v in enumerate(acc)] if e<len(acc) else acc+[0 for _ in range(e-len(acc))]+[1], L, [])
[0, 4, 4, 2, 1, 2]
An explanation:
we start with an empty acc list;
if the next element e of L is lower than the size of acc, we just update this element: v+(i==e) means v+1 if the index i of acc is the current element e, otherwise the previous value v;
if the next element e of L is greater or equals to the size of acc, we have to expand acc to host the new 1.
The elements do not have to be sorted (itertools.groupby). You'll get weird results if you have negative numbers.
Another approach of doing this, albeit by using a heavier but powerful library - NLTK.
import nltk
fdist = nltk.FreqDist(a)
fdist.values()
fdist.most_common()
Found another way of doing this, using sets.
#ar is the list of elements
#convert ar to set to get unique elements
sock_set = set(ar)
#create dictionary of frequency of socks
sock_dict = {}
for sock in sock_set:
sock_dict[sock] = ar.count(sock)
For an unordered list you should use:
[a.count(el) for el in set(a)]
The output is
[4, 4, 2, 1, 2]
Yet another solution with another algorithm without using collections:
def countFreq(A):
n=len(A)
count=[0]*n # Create a new list initialized with '0'
for i in range(n):
count[A[i]]+= 1 # increase occurrence for value A[i]
return [x for x in count if x] # return non-zero count
num=[3,2,3,5,5,3,7,6,4,6,7,2]
print ('\nelements are:\t',num)
count_dict={}
for elements in num:
count_dict[elements]=num.count(elements)
print ('\nfrequency:\t',count_dict)
You can use the in-built function provided in python
l.count(l[i])
d=[]
for i in range(len(l)):
if l[i] not in d:
d.append(l[i])
print(l.count(l[i])
The above code automatically removes duplicates in a list and also prints the frequency of each element in original list and the list without duplicates.
Two birds for one shot ! X D
This approach can be tried if you don't want to use any library and keep it simple and short!
a = [1,1,1,1,2,2,2,2,3,3,4,5,5]
marked = []
b = [(a.count(i), marked.append(i))[0] for i in a if i not in marked]
print(b)
o/p
[4, 4, 2, 1, 2]

Categories

Resources