I have a number of lists of lists stored in a dictionary. I want to find the intersection of the sub-lists (i.e. intersection of dict[i][j]) for all keys of the dictionary. )
For example, if the dictionary stored sets of tuples instead, I could use the code:
set.intersection(*[index[key] for key in all_keys])
What is an efficient way to do this? One way I tried was to first convert each list of lists into a set of tuples and then taking the intersection of those, but this is rather clunky.
Example:
Suppose the dictionary of lists of lists is
dict = {}
dict['A'] = [[1, 'charlie'], [2, 'frankie']]
dict['B'] = [[1, 'charlie'], [2, 'chuck']]
dict['C'] = [[2, 'chuck'], [1, 'charlie']]
then I want to return
[1, 'charlie']
(maybe as a tuple, doesn't have to be list)
EDIT: I just found an okay way to do it, but it's not very 'pythonic'
def search(index, search_words):
rv = {tuple(t) for t in index[search_words[0]]}
for word in search_words:
rv = rv.intersection({tuple(t) for t in index[word]})
return rv
Let's call your dictionary of lists of lists d:
>>> d = {'A': [[1, 'charlie'], [2, 'frankie']], 'B': [[1, 'charlie'], [2, 'chuck']], 'C': [[2, 'chuck'], [1, 'charlie']]}
I am calling it d because dict is a builtin and we would prefer not to overwrite it.
Now, find the intersection:
>>> set.intersection( *[ set(tuple(x) for x in d[k]) for k in d ] )
set([(1, 'charlie')])
How it works
set(tuple(x) for x in d[k])
For key k, this forms a set out of tuples of the elements in d[k]. Taking k='A' for example:
>>> k='A'; set(tuple(x) for x in d[k])
set([(2, 'frankie'), (1, 'charlie')])
[ set(tuple(x) for x in d[k]) for k in d ]
This makes a list of the sets from the step above. Thus:
>>> [ set(tuple(x) for x in d[k]) for k in d ]
[set([(2, 'frankie'), (1, 'charlie')]),
set([(2, 'chuck'), (1, 'charlie')]),
set([(2, 'chuck'), (1, 'charlie')])]
set.intersection( *[ set(tuple(x) for x in d[k]) for k in d ] )
This gathers the intersection of the three sets as above:
>>>set.intersection( *[ set(tuple(x) for x in d[k]) for k in d ] )
set([(1, 'charlie')])
[item for item in A if item in B+C]
Your usecase demands the use of reduce function. But, quoting the BDFL's take on reduce,
So now reduce(). This is actually the one I've always hated most, because, apart from a few examples involving + or *, almost every time I see a reduce() call with a non-trivial function argument, I need to grab pen and paper to diagram what's actually being fed into that function before I understand what the reduce() is supposed to do. So in my mind, the applicability of reduce() is pretty much limited to associative operators, and in all other cases it's better to write out the accumulation loop explicitly.
So, what you have got is already 'pythonic'.
I would write the program like this
>>> d = {'A': [[1, 'charlie'], [2, 'frankie']],
... 'B': [[1, 'charlie'], [2, 'chuck']],
... 'C': [[2, 'chuck'], [1, 'charlie']]}
>>> values = (value for value in d.values())
>>> result = {tuple(item) for item in next(values)}
>>> for item in values:
... result &= frozenset(tuple(items) for items in item)
>>> result
set([(1, 'charlie')])
from collections import defaultdict
d = {'A': [[1, 'charlie'], [2, 'frankie']], 'B': [[1, 'charlie'], [2, 'chuck']], 'C': [[2, 'chuck'], [1, 'charlie']]}
frequency = defaultdict(set)
for key, items in d.iteritems():
for pair in items:
frequency[tuple(pair)].add(key)
output = [
pair for pair, occurrances in frequency.iteritems() if len(d) == len(occurrances)
]
print output
Related
I have a list of lists all of the same length. I would like to segment the first list into contiguous runs of a given value. I would then like to segment the remaining lists to match the segments generated from the first list.
For example:
Given value: 2
Given list of lists: [[0,0,2,2,2,1,1,1,2,3], [1,2,3,4,5,6,7,8,9,10], [1,1,1,1,1,1,1,1,1,1]
Return: [ [[2,2,2],[2]], [[3,4,5],[9]], [[1,1,1],[1]] ]
The closest I have gotten is to get the indices by:
>>> import itertools
>>> import operator
>>> x = 2
>>> L = [[0,0,2,2,2,1,1,1,2,3],[1,2,3,4,5,6,7,8,9,10],[1,1,1,1,1,1,1,1,1,1]]
>>> I = [[i for i,value in it] for key,it in itertools.groupby(enumerate(L[0]), key=operator.itemgetter(1)) if key == x]
>>> print I
[[2, 3, 4], [8]]
This code was modified from another question on this site.
I would like to find the most efficient way possible, since these lists may be very long.
EDIT:
Maybe if I place the lists one on top of each other it might be clearer:
[[0,0,[2,2,2],1,1,1,[2],3], -> [2,2,2],[2]
[1,2,[3,4,5],6,7,8,[9],10],-> [3,4,5],[9]
[1,1,[1,1,1],1,1,1,[1],1]] -> [1,1,1],[1]
You can use groupby to create a list of groups in the form of a tuple of starting index and length of the group, and use this list to extract the values from each sub-list:
from itertools import groupby
from operator import itemgetter
def match(L, x):
groups = [(next(g)[0], sum(1 for _ in g) + 1)
for k, g in groupby(enumerate(L[0]), key=itemgetter(1)) if k == x]
return [[lst[i: i + length] for i, length in groups] for lst in L]
so that:
match([[0,0,2,2,2,1,1,1,2,3], [1,2,3,4,5,6,7,8,9,10], [1,1,1,1,1,1,1,1,1,1]], 2)
returns:
[[[2, 2, 2], [2]], [[3, 4, 5], [9]], [[1, 1, 1], [1]]]
l=[[0,0,2,2,2,1,1,1,2,3], [1,2,3,4,5,6,7,8,9,10], [1,1,1,1,1,1,1,1,1,1]]
temp=l[0]
value=2
dict={}
k=-1
prev=-999
for i in range(0,len(temp)):
if(temp[i]==value):
if(prev!=-999 and prev==i-1):
if(k in dict):
dict[k].append(i)
else:
dict[k]=[i]
else:
k+=1
if(k in dict):
dict[k].append(i)
else:
dict[k]=[i]
prev=i
output=[]
for i in range(0,len(l)):
single=l[i]
final=[]
for keys in dict: #{0: [2, 3, 4], 1: [8]}
ans=[]
desired_indices=dict[keys]
for j in range(0,len(desired_indices)):
ans.append(single[desired_indices[j]])
final.append(ans)
output.append(final)
print(output) #[[[2, 2, 2], [2]], [[3, 4, 5], [9]], [[1, 1, 1], [1]]]
This seems to be one of the approach, this first creates the dictionary of contagious elements and then looks for that keys in every list and stores in output.
Alright I searched, but I did not find an answer to my specific problem. I am creating a dictionary of lists. I have while loop creating a few lists of values as well as a name to associate with it:
dict = {}
list = [1, 2]
name = key1
list = [3, 4]
name = key1
If the name is the same, the list will be appended to the existing set of values for the key. Here is the code I have:
if key in dict:
dict[key].append(list)
else:
dict[key] = list
This is the output I want:
dictionary = {'key1': [1, 2], [3, 4]}
However I keep getting this output:
dictionary = {'key1': [1, 2, [3, 4]]}
where the second list to the key is being put inside of the first list.
This is a very common error. I am fairly certain that you are doing:
list1.append(list2)
Instead, you want to do:
list1.extend(list2)
Here is a very useful resource
However, since you want [[1,2], [3,4]] instead of [1,2,3,4], you should do:
if key in d1:
d1[key].append(l1)
else:
d1[key] = [l1]
That is because you are appending a list to a list each time. You need to use extend instead. Code:
keys = ['name1', 'name2', 'name2', 'name1']
somelist = [1, 2, 3]
d = {}
for k in keys:
d.setdefault(k, []).extend(somelist)
You need a list of list in fact, your output will look like this:
dictionary = {'key1': [[1, 2], [3, 4]]}
To have a key associated to multiple values you could use this line:
dictionary.setdefault(key, []).append(a_list)
setdefault will associate the key to the default value [] if the key is not present in your dictionary.
Also you should avoid using dict or list to define your own variables, they are built-in and you are redefining them.
Edit
To make it obvious to the reader maybe this could help, its the output of an interative python session:
>>> d = {}
>>> d.setdefault('key1', []).append([1, 2])
>>> d.setdefault('key1', []).append([3, 4])
>>> d
{'key1': [[1, 2], [3, 4]]}
I hope not misunderstand , since dictionary = {'key1': [1, 2], [3, 4]} is poorly expressed
def add2dict(d, l, k):
if not k in d:
dict[k] = []
d[k].append(l)
dict = {}
add2dict(dict, [1,2], "key1")
add2dict(dict, [3,4], "key1")
result in dict:
{'key1': [[1, 2], [3, 4]]}
I have a array filled with e-mail addresses which change constantly. e.g.
mailAddressList = ['chip#plastroltech.com','spammer#example.test','webdude#plastroltech.com','spammer#example.test','spammer#example.test','support#plastroltech.com']
How do I find multiple occurrences of the same string in the array and output it's indexes?
just group indexes by email and print only those items, where lenght of index list is greater than 1:
from collections import defaultdict
mailAddressList = ['chip#plastroltech.com',
'spammer#example.test',
'webdude#plastroltech.com',
'spammer#example.test',
'spammer#example.test',
'support#plastroltech.com'
]
index = defaultdict(list)
for i, email in enumerate(mailAddressList):
index[email].append(i)
print [(email, positions) for email, positions in index.items()
if len(positions) > 1]
# [('spammer#example.test', [1, 3, 4])]
Try this:
query = 'spammer#example.test''
indexes = [i for i, x in enumerate(mailAddressList) if x == query]
Output:
[1, 3, 4]
In [7]: import collections
In [8]: q=collections.Counter(mailAddressList).most_common()
In [9]: indexes = [i for i, x in enumerate(mailAddressList) if x == q[0][0]]
In [10]: indexes
Out[10]: [1, 3, 4]
note: solutions submitted before are more pythonic than mine. but in my opinon, lines that i've written before are easier to understand. i simply will create a dictionary, then will add mail adresses as key and the indexes as value.
first declare an empty dictionary.
>>> dct = {}
then iterate over mail adresses (m) and their indexes (i) in mailAddressList and add them to dictionary.
>>> for i, m in enumerate(mailAddressList):
... if m not in dct.keys():
... dct[m]=[i]
... else:
... dct[m].append(i)
...
now, dct looks liike this.
>>> dct
{'support#plastroltech.com': [5], 'webdude#plastroltech.com': [2],
'chip#plastroltech.com': [0], 'spammer#example.test': [1, 3, 4]}
there are many ways to grab the [1,3,4]. one of them (also not so pythonic :) )
>>> [i for i in dct.values() if len(i)>1][0]
[1, 3, 4]
or this
>>> [i for i in dct.items() if len(i[1])>1][0] #you can add [1] to get [1,3,4]
('spammer#example.test', [1, 3, 4])
Here's a dictionary comprehension solution:
result = { i: [ k[0] for k in list(enumerate(mailAddressList)) if k[1] == i ] for j, i in list(enumerate(mailAddressList)) }
# Gives you: {'webdude#plastroltech.com': [2], 'support#plastroltech.com': [5], 'spammer#example.test': [1, 3, 4], 'chip#plastroltech.com': [0]}
It's not ordered, of course, since it's a hash table. If you want to order it, you can use the OrderedDict collection. For instance, like so:
from collections import OrderedDict
final = OrderedDict(sorted(result.items(), key=lambda t: t[0]))
# Gives you: OrderedDict([('chip#plastroltech.com', [0]), ('spammer#example.test', [1, 3, 4]), ('support#plastroltech.com', [5]), ('webdude#plastroltech.com', [2])])
This discussion is less relevant, but it might also prove useful to you.
mailAddressList = ["chip#plastroltech.com","spammer#example.test","webdude#plastroltech.com","spammer#example.test","spammer#example.test","support#plastroltech.com"]
print [index for index, address in enumerate(mailAddressList) if mailAddressList.count(address) > 1]
prints [1, 3, 4], the indices of the addresses occuring more than once in the list.
I have the following list:
x=['a','3','4','b','1','2','c','4','5']
How can i make the following dictionary:
b = {'a':[3,4],'b':[1,2],'c':[4,5]}
I tried the following:
Category = defaultdict(int)
for i in a:
if Varius.is_number(i)==False:
Category[i]=[]
keys.append(i)
else:
Category[keys(i)] = i
The keys are created but after i have problem to insert the values.(is_number is a function which check if the value of the list is number or string).First day away of MATLAB.First day in Python..
Here an example that actually uses the feature that defaultdict provides over the normal dict:
from collections import defaultdict
x=['a','3','4','b','1','2','c','4','5']
key='<unknown>' # needed if the first value of x is a number
category = defaultdict(list) # defaultdict with list
for i in x:
if i.isalpha():
key = i;
else:
category[key].append(i) # no need to initialize with an empty list
print category
Also: you should use lower case names for class instances. Uppercase names are usually reserved for classes. Read pep8 for a style guide.
If the key/value entries in your list x are evenly spaced, here's an alternative way to build the dictionary. It uses a few of Python's built in features and functions:
>>> keys = x[::3]
>>> values = [map(int, pair) for pair in zip(x[1::3], x[2::3])]
>>> dict(zip(keys, values))
{'a': [3, 4], 'b': [1, 2], 'c': [4, 5]}
To explain what's being used here:
list slicing to create new lists from x: x[start:stop:step]
here zip takes two lists and makes a list of tuples containing the n-th elements of each list
map(int, pair) turns a tuple of digit strings into a list of integers
values is constructed with list comprehension - the map function is applied to each pair
dict turns a list of pairs into dictionary keys/values
You can use itertools.groupby :
>>> from itertools import groupby
>>> l=[list(g) for k,g in groupby(x,lambda x : x.isalpha())]
>>> p=[l[i:i+2] for i in range(0,len(l),2)]
>>> {i[0]:map(int,j) for i,j in p}
{'a': [3, 4], 'c': [4, 5], 'b': [1, 2]}
Assuming you have a key on the first positions of your list (x) and that after a letter, there will be a number:
from collections import defaultdict
x=['a','3','4','b','1','2','c','4','5']
key = x[0]
Category = defaultdict(int)
for i in x:
if i.isalpha():
key = i;
else:
Category[key].append(i)
print Category
Using itertools.groupby and some iterator stuff:
>>> from itertools import groupby
>>> it = (next(g) if k else map(int, g) for k, g in groupby(x, str.isalpha))
>>> {k: next(it) for k in it}
{'a': [3, 4], 'c': [4, 5], 'b': [1, 2]}
Here the first iterator will yield something like:
>>> [next(g) if k else map(int, g) for k, g in groupby(x, str.isalpha)]
['a', [3, 4], 'b', [1, 2], 'c', [4, 5]]
Now as this iterator is going to yield key-value alternately, we can loop over this iterator and get the next item from it(i.e the value) using next()
>>> it = (next(g) if k else map(int, g) for k, g in groupby(x, str.isalpha))
>>> for k in it:
print k,'-->' ,next(it)
...
a --> [3, 4]
b --> [1, 2]
c --> [4, 5]
There's another way to consume this iterator that is using zip, but it's a little hard to understand IMO:
>>> it = (next(g) if k else map(int, g) for k, g in groupby(x, str.isalpha))
>>> dict(zip(*[it]*2))
{'a': [3, 4], 'c': [4, 5], 'b': [1, 2]}
Simple solution using just dict and list:
>>> category = {}
>>> for i in x:
... if i.isalpha():
... items = category[i] = []
... elif category:
... items.append(i)
...
>>> print(category)
{'c': ['4', '5'], 'a': ['3', '4'], 'b': ['1', '2']}
I have a list in Python filled with arrays.
([4,1,2],[1,2,3],[4,1,2])
How do I remove the duplicate array?
Very simple way to remove duplicates (if you're okay with converting to tuples/other hashable item) is to use a set as an intermediate element.
lst = ([4,1,2],[1,2,3],[4,1,2])
# convert to tuples
tupled_lst = set(map(tuple, lst))
lst = map(list, tupled_lst)
If you have to preserve order or don't want to convert to tuple, you can use a set to check if you've seen the item before and then iterate through, i.e.,
seen = set()
def unique_generator(lst)
for item in lst:
tupled = tuple(item)
if tupled not in seen:
seen.add(tupled)
yield item
lst = list(unique_generator(lst))
This isn't great python, but you can write this as a crazy list comprehension too :)
seen = set()
lst = [item for item in lst if not(tuple(item) in seen or seen.add(tuple(item)))]
If order matters:
>>> from collections import OrderedDict
>>> items = ([4,1,2],[1,2,3],[4,1,2])
>>> OrderedDict((tuple(x), x) for x in items).values()
[[4, 1, 2], [1, 2, 3]]
Else it is much simpler:
>>> set(map(tuple, items))
set([(4, 1, 2), (1, 2, 3)])
l = ([4,1,2],[1,2,3],[4,1,2])
uniq = []
for i in l:
if not i in uniq:
uniq.append(i)
print('l=%s' % str(l))
print('uniq=%s' % str(uniq))
which produces:
l=([4, 1, 2], [1, 2, 3], [4, 1, 2])
uniq=[[4, 1, 2], [1, 2, 3]]
Use sets to keep track of seen items, but as sets can only contain hashable items so you may have to convert the items of your tuple to some hashable value first( tuple in this case) .
Sets provide O(1) lookup, so overall complexity is going to be O(N)
This generator function will preserve the order:
def solve(lis):
seen = set()
for x in lis:
if tuple(x) not in seen:
yield x
seen.add(tuple(x))
>>> tuple( solve(([4,1,2],[1,2,3],[4,1,2])) )
([4, 1, 2], [1, 2, 3])
If the order doesn't matter then you can simply use set() here:
>>> lis = ([4,1,2],[1,2,3],[4,1,2]) # this contains mutable/unhashable items
>>> set( tuple(x) for x in lis) # apply tuple() to each item, to make them hashable
set([(4, 1, 2), (1, 2, 3)]) # sets don't preserve order
>>> lis = [1, 2, 2, 4, 1] #list with immutable/hashable items
>>> set(lis)
set([1, 2, 4])