Checking pairs in a list? - python

Basically I have a program which will take a number and factor it down to the smallest number, being 2, 3, 5, 7, and so on. I'm having trouble figuring out how to check if there are one or multiple pairs of numbers inside of a list. For example.
myList = [1,1,1,4,5,6,6,3,3,1]
in myList, there are four 1's which would be two pairs of two. Pairs then need to be thrown into another list but instead of adding both numbers making it a pair it only needs to have one of those numbers of the pair.
For example:
myList = [1,1,1,4,5,6,6,3,3,1]
doubles = [1,1,6,3]
So, there are four ones. Which in turn make two pairs of two, which would add into a list, but only one number needs to be added to a list representing a pair.

This is similar to qarma's first solution, but it avoids the double for loop.
from collections import Counter
my_list = [1, 1, 1, 4, 5, 6, 6, 3, 3, 1, 7, 7, 7]
doubles = []
for k, v in Counter(my_list).items():
doubles.extend([k] * (v // 2))
print(doubles)
output
[1, 1, 6, 3, 7]

Something like this?
>>> myList = [1,1,1,4,5,6,6,3,3,1]
>>> mySet = set()
>>> doubles = []
>>> for i in myList:
... if i in mySet:
... doubles.append(i)
... mySet.remove(i)
... else:
... mySet.add(i)
...
>>> doubles
[1, 6, 3, 1]
Note - This doesn't preserve the order you seem to have expected in your question, i.e. [1, 1, 6, 3].

simple solution
[k for k, v in collections.Counter([1,1,1,4,5,6,6,3,3,1]).items() for _i in range(v // 2)]
[1, 1, 3, 6]
Counter is a kind of a dict, thus doesn't keep insertion order. Also, it compresses input, so for example, input like 1, 1, 3, 3, 1, 1 is guaranteed to result in either 1, 1, 3 or 3, 1, 1 and never 1, 3, 1.
more complex
In [7]: def pairs(s):
...: queue = set()
...: for i in s:
...: if i in queue:
...: yield i
...: queue.remove(i)
...: else:
...: queue.add(i)
...:
In [8]: list(pairs([1,1,1,4,5,6,6,3,3,1]))
Out[8]: [1, 6, 3, 1]
This, preserves order of pairs, but pairs are ordered according to last item in a pair, e.g. 1, 9, 9, 1 becomes 9, 1.
even more complex
In [12]: def pairs(s):
...: incomplete = dict()
...: done = []
...: for i, v in enumerate(s):
...: if v in incomplete:
...: done.append((incomplete[v], v))
...: del incomplete[v]
...: else:
...: incomplete[v] = i
...: return [v[1] for v in sorted(done)]
...:
...:
In [13]: pairs([1,1,1,4,5,6,6,3,3,1])
Out[13]: [1, 1, 6, 3]
Here, original position of first element of each pair is kept as a value in the incomplete dict, which allows to reconstruct original order according to first item in a pair.

using defaultdictionary
from collections import defaultdict
def func(lis):
dic = defaultdict(int)
for i in lis:
dic[i]+=1
list1 =[]
for k,v in dic.items():
if v>=2:
list1.append([k]*(v//2))
return list1
myList = [1,1,1,4,5,6,6,3,3,1]
data = [j for i in func(myList) for j in i]
print(data)
# output
# [1,1,6,3]

Another option with Counter:
from collections import Counter
myList = [1,1,1,4,5,6,6,3,3,1]
dupes = Counter({k: v // 2 for k, v in Counter(myList).items()})
sorted(dupes.elements())
# [1, 1, 3, 6]

Related

Sort a list by frequency and value

I am trying to solve the following problem: a function takes a list A. The results must be a ordered list of list. Each list contains the elements which have the same frequency in the original list A.
Example:
Input: [3, 1, 2, 2, 4]
Output: [[1, 3, 4], [2, 2]]
I managed to sort the initial list A and determine how the frequency of an element.
However, I do not know how to split the original list A based on the frequencies.
My code:
def customSort(arr):
counter = Counter(arr)
y = sorted(arr, key=lambda x: (counter[x], x))
print(y)
x = Counter(arr)
a = sorted(x.values())
print()
customSort([3,1,2,2,4])
My current output:
[1, 3, 4, 2, 2]
[1, 1, 1, 2]
You can use a defaultdict of lists and iterate your Counter:
from collections import defaultdict, Counter
def customSort(arr):
counter = Counter(arr)
dd = defaultdict(list)
for value, count in counter.items():
dd[count].extend([value]*count)
return dd
res = customSort([3,1,2,2,4])
# defaultdict(list, {1: [3, 1, 4], 2: [2, 2]})
This gives additional information, i.e. the key represents how many times the values in the lists are seen. If you require a list of lists, you can simply access values:
res = list(res.values())
# [[3, 1, 4], [2, 2]]
Doing the grunt work suggested by Scott Hunter (Python 3):
#!/usr/bin/env python3
from collections import Counter
def custom_sort(arr):
v = {}
for key, value in sorted(Counter(arr).items()):
v.setdefault(value, []).append(key)
return [v * k for k,v in v.items()]
if __name__ == '__main__':
print(custom_sort([3, 1, 2, 2, 4])) # [[1, 3, 4], [2, 2]]
For Python 2.7 or lower use iteritems() instead of items()
Partially taken from this answer
Having sorted the list as you do:
counter = Counter(x)
y = sorted(x, key=lambda x: (counter[x], x))
#[1, 3, 4, 2, 2]
You could then use itertools.groupby, using the result from Counter(x) in the key argument to create groups according to the counts:
[list(v) for k,v in groupby(y, key = lambda x: counter[x])]
#[[1, 3, 4], [2, 2]]
Find your maximum frequency, and create a list of that many empty lists.
Loop over your values, and add each to the element of the above corresponding to its frequency.
There might be something in Collections that does at least part of the above.
Another variation of the same theme, using a Counter to get the counts and then inserting the elements into the respective position in the result list-of-lists. This retains the original order of the elemens (does not group same elements together) and keeps empty lists for absent counts.
>>> lst = [1,4,2,3,4,3,2,5,4,4]
>>> import collections
>>> counts = collections.Counter(lst)
>>> res = [[] for _ in range(max(counts.values()))]
>>> for x in lst:
... res[counts[x]-1].append(x)
...
>>> res
[[1, 5], [2, 3, 3, 2], [], [4, 4, 4, 4]]
A bit late to the party, but with plain Python:
test = [3, 1, 2, 2, 4]
def my_sort(arr):
count = {}
for x in arr:
if x in count:
count[x] += 1
else:
count[x] = 0
max_frequency = max(count.values()) + 1
res = [[] for i in range(max_frequency)]
for k,v in count.items():
for j in range(v + 1):
res[v].append(k)
return res
print(my_sort(test))
Using only Pythons built-in functions, no imports and a single for loop.
l1= []
l2 = []
def customSort(mylist):
sl = sorted(mylist)
for i in sl:
n = sl.count(i)
if n > 1:
l1.append(i)
if i not in l1:
l2.append(i)
return [l2, l1]
print(customSort([3, 1, 2, 2, 4]))
Output:
[[1, 3, 4], [2, 2]]

Getting sublist with repeated elements in Python [duplicate]

I faced some problem with solving the next problem:
We have a list of elements (integers), and we should return a list consisting of only the non-unique elements in this list. Without changing order of the list
I think the best way is to delete or remove all unique element.
Take note that I just start to learn python and would like only the simplest solutions.
Here is my code:
def checkio(data):
for i in data:
if data.count(i) == 1: #if element seen in the list just ones, we delet this el
ind = data.index(i)
del data[ind]
return data
Your function can be made to work by iterating over the list in reverse:
def checkio(data):
for index in range(len(data) - 1, -1, -1):
if data.count(data[index]) == 1:
del data[index]
return data
print(checkio([3, 3, 5, 8, 1, 4, 5, 2, 4, 4, 3, 0]))
[3, 3, 5, 4, 5, 4, 4, 3]
print(checkio([1, 2, 3, 4]))
[]
This works, because it only deletes numbers in the section of the list that has already been iterated over.
Just I used list Comprehensions.
def checkio(data):
a=[i for i in data if data.count(i)>1]
return a
print checkio([1,1,2,2,1,1,1,3,4,5,6,7,8])
You can implement a OrderedCounter, eg:
from collections import OrderedDict, Counter
class OrderedCounter(Counter, OrderedDict):
pass
data = [1, 3, 1, 2, 3, 5, 8, 1, 5, 2]
duplicates = [k for k, v in OrderedCounter(data).items() if v > 1]
# [1, 3, 2, 5]
So you count the occurrence of each value, then filter on if it has a frequency of more than one. Inheriting from OrderedDict means the order of the original elements is preserved.
Going by comments, you want all duplicated elements reserved, so you can pre-build a set of the duplicate entries, then re-iterate your original list, eg:
from collections import Counter
data = [1, 3, 1, 2, 3, 5, 8, 1, 5, 2]
duplicates = {k for k, v in Counter(data).items() if v > 1}
result = [el for el in data if el in duplicates]
# [1, 3, 1, 2, 3, 5, 1, 5, 2]
Try this:
>>> a=[1,2,3,3,4,5,6,6,7,8,9,2,0,0]
>>> a=[i for i in a if a.count(i)>1]
>>> a
[2, 3, 3, 6, 6, 2, 0, 0]
>>> a=[1, 2, 3, 1, 3]
>>> a=[i for i in a if a.count(i)>1]
>>> a
[1, 3, 1, 3]
>>> a=[1, 2, 3, 4, 5]
>>> a=[i for i in a if a.count(i)>1]
a
[]
def checkio(data):
lis = []
for i in data:
if data.count(i)>1:
lis.append(i)
print(lis)
checkio([1,2,3,3,2,1])
Yeah it's a bit late to contribute to this thread but just wanted to put it there on the net for anyone else use.
Following what you have started, iterating on the list of integers, but not counting or deleting elements, try just testing if the element has already been seen, append it to a list of duplicated elements:
def checkio(data):
elements = []
duplicates = []
for i in data:
if i not in elements:
elements.append(i)
else:
if i not in duplicates:
duplicates.append(i)
return duplicates
d = [1, 3, 1, 2, 3, 5, 8, 1, 5, 2]
print (checkio(d))
#[1, 3, 5, 2]
numbers = [1, 1, 1, 1, 3, 4, 9, 0, 1, 1, 1]
x=set(numbers)
print(x)
You can use the set key word too to get the desired solution.
I used an integer and bool to check every time the list was modified within a while loop.
rechecks = 1
runscan = True
while runscan == True:
for i in data:
if data.count(i) <2:
data.remove(i)
rechecks+=1
#need to double check now
if rechecks >0:
runscan = True
rechecks-=1
else:
runscan = False
return data
Would it not be easier to generate a new list?
def unique_list(lst):
new_list = []
for value in lst:
if value not in new_list:
new_list.append(value)
return new_list
lst = [1,2,3,1,4,5,1,6,2,3,7,8,9]
print(unique_list(lst))
Prints [1,2,3,4,5,6,7,8,9]

how to convert a list in python to a set according to index number

I have a list y=[0,2,1,2,1,1,2,1] and it has 8 elements (from 0 to 7).
And since it has three unique elements, so three sets will be created.
I want the output to be
s1={0}
s2={1,3,6}
s3={2,4,5,7}
If I understood correctly, what you want to do is to get a set for each unique value in your list that will return indexes of all occurrences of this value in the list.
We can't tell how many sets we will need, therefore we should create a new set for each unique value and hold these sets in a list.
y = [0,2,1,2,1,1,2,1]
y_set = set(y)
set_list = []
for unique_value in y_set:
new_set = set()
for i, value in enumerate(y):
if unique_value == value:
new_set.add(i)
set_list.append(new_set)
This is the result you will get from this approach:
[{0}, {2, 4, 5, 7}, {1, 3, 6}]
You can use enumerate and defaultdict:
from collections import defaultdict
my_dict = defaultdict(list)
for index, element in enumerate(y):
my_dict[element].append(index)
result = my_dict.values()
I think it's better to use the value as the key, it's more clear for further steps.
y=[0,2,1,2,1,1,2,1]
dict = {}
for i, x in enumerate(y):
if x not in dict.keys():
dict[x] = [i]
else:
dict[x].append(i)
print dict
{0: [0], 1: [2, 4, 5, 7], 2: [1, 3, 6]}
And if you prefer to get the exactly output as mentioned -
from collections import OrderedDict
y= [0, 2, 1, 2, 1, 1, 2, 1]
dict2 = OrderedDict()
dict_seq = OrderedDict()
sequence = 1
list = []
for i, x in enumerate(y):
if x not in dict2.keys():
dict2[x] = [i]
dict_seq['S{0}'.format(sequence)] = dict2[x]
sequence+=1
else:
dict2[x].append(i)
print("dict2 = {0}".format(dict2))
for key, value in dict_seq.items():
print("{0} = {1}\n".format(key, value))
dict2 = OrderedDict([(0, [0]), (2, [1, 3, 6]), (1, [2, 4, 5, 7])])
S1 = [0]
S2 = [1, 3, 6]
S3 = [2, 4, 5, 7]
You can try manual approach:
y=[0,2,1,2,1,1,2,1]
groub_by={}
for i,j in enumerate(y):
if j not in groub_by:
groub_by[j]=[i]
else:
groub_by[j].append(i)
print(groub_by.values())
or you can also try itertools grouby approach:
list_with_index=[(j,i) for i,j in enumerate(y)]
import itertools
for i,j in itertools.groupby(sorted(list_with_index),key=lambda x:x[0]):
print(list(map(lambda x:x[1],j)))
output:
[0]
[2, 4, 5, 7]
[1, 3, 6]
try like this :
from collections import defaultdict
lst = [0,2,1,2,1,1,2,1]
lst_indxs = dict(enumerate(lst))
final_dict = defaultdict(list)
for i,j in lst_indxs.items():
final_dict[j].append(i)
print(dict(final_dict))
Try something like this:
y = [0,2,1,2,1,1,2,1]
d = {}
for i in set(y):
d[i] = []
for j in range(len(y)):
d[y[j]].append(j)
print d
Output:
{0: [0], 1: [2, 4, 5, 7], 2: [1, 3, 6]}

Deleting items witht the same index from multiple lists inside a dictionary

I have a dictionary consisting of lists, for example
mydict={}
mydict['a']=[1,0,0,0,1,1,1,1,1,0,0]
mydict['b']=[2,4,5,6,8,7,5,4,5,6,7]
mydict['c']=[4,5,6,4,6,8,4,3,5,8,7]
Now say that if in 'a' the value is 0, I want to delete the item with that index from every list in the libraby. So for the first 0 that would be removing 0 from 'a', 4 from 'b' and 5 from 'c', for the second 0: 0 from 'a', 5 from 'b' and 6 from 'c' etc.
I've been trying to do this in many different ways, but I just do not manage to find the right way. Does anyone know how to do this? Would be great!
Using itertools.compress:
>>> import itertools
>>>
>>> mydict={}
>>> mydict['a']=[1,0,0,0,1,1,1,1,1,0,0]
>>> mydict['b']=[2,4,5,6,8,7,5,4,5,6,7]
>>> mydict['c']=[4,5,6,4,6,8,4,3,5,8,7]
>>> # ^ ^ ^ ^ ^ ^
>>> mask = mydict['a']
>>> for key, value in mydict.items():
... mydict[key] = list(itertools.compress(value, mask))
...
>>> mydict
{'a': [1, 1, 1, 1, 1, 1], 'c': [4, 6, 8, 4, 3, 5], 'b': [2, 8, 7, 5, 4, 5]}
Here is one possible solution:
mydict={}
mydict['a']=[1,0,0,0,1,1,1,1,1,0,0]
mydict['b']=[2,4,5,6,8,7,5,4,5,6,7]
mydict['c']=[4,5,6,4,6,8,4,3,5,8,7]
toRemove = [i for i,x in enumerate(mydict['a']) if x == 0]
for key in mydict.keys():
mydict[key] = [x for i,x in enumerate(mydict[key]) if i not in toRemove]
print(mydict)
>>>{'a': [1, 1, 1, 1, 1, 1], 'c': [4, 6, 8, 4, 3, 5], 'b': [2, 8, 7, 5, 4, 5]}
Assuming you also want to remove 0s from the 'a' list. If not adding a simple condition will help.
Alternatively, even more concise but less readable approach:
mydict = {key:[x for i,x in enumerate(value) if i not in indices]
for key, value in mydict.iteritems()}
Simple loop approach:
index = mydict['a'].index(0)
for value in mydict.values():
del value[index]
but that removes only one item.
For multiple items:
indices = {i for i, v in enumerate(mydict['a']) if v == 0}
mydict = {k: [x for i, x in enumerate(v) if i not in indices]
for k, v in mydict.items()}
Use dictionary.iteritems() when using Python 2.
Using itertools.compress() supporting arbitrary values:
from itertools import compress
value_to_remove = 0
mask = [v != value_to_remove for v in mydict['a']]
value_to_remove = 0
mydict = {k: list(compress(v, mask)) for k, v in mydict.items()}

Finding a set in a Dictionary fitting a certain criteria

I have a dictionary. If V is in DICT[K] then someFunc(k, v) and someFunc(v, k) both return true (and K is in DICT[V]). The dictionary could look like this:
{
1: [2, 3, 5],
2: [3, 1, 5],
3: [1, 2],
5: [2, 1],
}
I want to find all sets of numbers of a certain length inside a dictionary that fit this criteria: someFunc(x, y) and someFunc(y, x) must be true for any pair in the dictionary. For example, for the dictionary I showed:
{1, 2, 3} would be a valid length 3 set. The criteria must be valid as all items contain every other item:
dict[1] contains 2, 3
dict[2] contains 1, 3
dict[3] contains 1, 2
What is the best way to find all such sets of a given length in a given dict, if I know that all valid sets must contain a given number.
from itertools import combinations
from collections import OrderedDict
def sf(k,v,d):
return (k in d[v]) and (v in d[k])
def lenN(d, n):
# create a list of unique items
l = list(OrderedDict.fromkeys(i for x in d for i in d[x]))
# collect matching groups
V = list()
for C in combinations(l, n):
for P in combinations(C, 2):
if not sf(P[0], P[1], d): break
else: V.append(C)
return V
d = { 1: [2, 3, 5], 2: [3, 1, 5], 3: [1, 2], 5: [2, 1], }
print lenN(d, 3)
Output
[(2, 3, 1), (2, 5, 1)]

Categories