Dictionary is not giving all elements - Python - python

I want to make dictionary from lists.
import numpy as np
a1 = [1,2,3,4,5,6,7,8,9]
b1 = ['a','b','c','d','e','f','g','h','i']
c1 = ['A','B','C','D','E','F','G','H','I']
array2 = np.array([a1,b1,c1]).tolist()
keys = ['name', 'type','description','logo']
print dict(zip(keys, zip(*array2)))
Output:
{'logo': ('4', 'd', 'D'), 'type': ('2', 'b', 'B'), 'name': ('1', 'a', 'A'), 'description': ('3', 'c', 'C')}
Why am i getting only 4 elements
Why the dictionary elements are in random order of 4,2,1,3 and why
not 1,2,3,4?

Why am I getting only 4 elements?
According to the zip documentation:
The returned list is truncated in length to the length of the shortest argument sequence.
Since keys only contains 4 elements, your zipped list resulting from zip(keys, zip(*array2)) will also contain 4 values, resulting in your dict only containing 4 values.
Why the dictionary elements are in random order?
The order of elements in a normal dictionary is not guaranteed. I think it's implementation specific, and usually based on the order of the keys' hashes. You should use OrderedDict if you want to maintain a specific order:
import numpy as np
from collections import OrderedDict
a1 = [1,2,3,4,5,6,7,8,9]
b1 = ['a','b','c','d','e','f','g','h','i']
c1 = ['A','B','C','D','E','F','G','H','I']
array2 = np.array([a1,b1,c1]).tolist()
keys = ['name', 'type','description','logo']
print OrderedDict(zip(keys, zip(*array2)))
# prints: OrderedDict([('name', ('1', 'a', 'A')), ('type', ('2', 'b', 'B')), ('description', ('3', 'c', 'C')), ('logo', ('4', 'd', 'D'))])

Related

Getting Unique Values and their Following Pairs from a List of Tuples

I have a list of tuples like this:
[
('a', 'AA'), # pair 1
('d', 'AA'), # pair 2
('d', 'a'), # pair 3
('d', 'EE'), # pair 4
('b', 'BB'), # pair 5
('b', 'CC'), # pair 6
('b', 'DD'), # pair 7
('c', 'BB'), # pair 8
('c', 'CC'), # pair 9
('c', 'DD'), # pair 10
('c', 'b'), # pair 11
('d', 'FF'), # pair 12
]
Each tuple in the list above shows a similar pair of items (or duplicate items). I need to create a dictionary in which keys will be one of the unique item from the tuples and values will be lists filled with all the other items that the key occurred in conjunction with. For example, 'a' is similar to 'AA'(pair 1), which in turn is similar to 'd'(pair 2) and 'd' is similar to 'EE' and 'FF' (pairs 4 and 12). same is the case with other items.
My expected output is:
{'a':['AA', 'd', 'EE', 'FF'], 'b':['BB', 'CC', 'DD', 'c']}
According to the tuples, ['a', 'AA', 'd', 'EE', 'FF'] are similar; so, any one of them can be the key, while the remaining items will be it's values. so, output can also be: {'AA':['a', 'd', 'EE', 'FF'], 'c':['BB', 'CC', 'DD', 'b']}. So, key of the output dict can be anything from the duplicate pairs.
How do I do this for a list with thousands of such tuples in a list?
As said in the comment, you seem to want to find the equivalence classes on the given relationship.
ecs = []
for a, b in data:
a_ec = next((ec for ec in ecs if a in ec), None)
b_ec = next((ec for ec in ecs if b in ec), None)
if a_ec:
if b_ec:
# Found equivalence classes for both elements, everything is okay
if a_ec is not b_ec:
# We only need one of them though
ecs.remove(b_ec)
a_ec.update(b_ec)
else:
# Add the new element to the found equivalence class
a_ec.add(b)
else:
if b_ec:
# Add the new element to the found equivalence class
b_ec.add(a)
else:
# First time we see either of these: make a new equivalence class
ecs.append({a, b})
# Extract a representative element and construct a dictionary
out = {
ec.pop(): ec
for ec in ecs
}
You can use 3 dictionaries, one for the output (out), one to track the seen values (seen), and one to map the equal keys (mapper):
out = {}
seen = {}
mapper = {}
for a, b in l:
if b in seen:
out.setdefault(seen[b], []).append(a)
mapper[a] = seen[b]
else:
out.setdefault(mapper.setdefault(a, a), []).append(b)
seen[b] = a
# remove duplicates
out = {k: list(dict.fromkeys(x for x in v if x!=k))
for k, v in out.items()}
Output:
{'a': ['AA', 'd', 'EE', 'FF'],
'b': ['BB', 'CC', 'DD', 'c']}
graph approach
Or you might want to approach this using a graph:
import networkx as nx
G = nx.from_edgelist(l)
out = {(x:=list(g))[0]: x[1:] for g in nx.connected_components(G)}
Output:
{'EE': ['d', 'a', 'FF', 'AA'],
'CC': ['b', 'c', 'BB', 'DD']}
Graph:

Converting list in list to dictionary

I got a list like this:
[['a','b','1','2']['c','d','3','4']]
and I want to convert this list to dictionary something looks like this:
{
('a','b'):('1','2'),
('c','d'):('3','4')
}
for example, ('a', 'b') & ('c','d') for key
and ('1','2') &('3','4') for value
so I used code something like this
new_dict = {}
for i, k in enumerate(li[0:2]):
new_dict[k] =[x1[i] for x1 in li[2:]]
print(new_dict)
,but it caused unhashable type error 'list'
I tried several other way, but it didn't work well..
Is there any way that I can fix it?
You can't have list as key, but tuple is possible. Also you don't need to slice on your list, but on the sublist.
You need the 2 first values sublist[:2] as key and the corresponding values is the sublist from index 2 sublist[2:]
new_dict = {}
for sublist in li:
new_dict[tuple(sublist[:2])] = tuple(sublist[2:])
print(new_dict) # {('a', 'b'): ('1', '2'), ('c', 'd'): ('3', '4')}
The same with dict comprehension
new_dict = {tuple(sublist[:2]): tuple(sublist[2:]) for sublist in li}
print(new_dict) # {('a', 'b'): ('1', '2'), ('c', 'd'): ('3', '4')}
li = [['a','b','1','2'],['c','d','3','4']]
new_dict = {}
for item in li:
new_dict[(item[0], item[1])] = (item[2], item[3])
I would use list-comprehension following way:
lst = [['a','b','1','2']['c','d','3','4']]
dct = dict([(tuple(i[:2]),tuple(i[2:])) for i in lst])
print(dct)
or alternatively dict-comprehension:
dct = {tuple(i[:2]):tuple(i[2:]) for i in lst}
Output:
{('a', 'b'): ('1', '2'), ('c', 'd'): ('3', '4')}
Note that list slicing produce lists, which are mutable and can not be used as dict keys, so I use tuple to convert these to immutable tuples.
You can do it with dict comprehension:
li = [
['a', 'b', '1', '2'],
['c', 'd', '3', '4'],
]
new_dict = {(i[0], i[1]): (i[2], i[3]) for i in li}
print(new_dict)
# result
{('a', 'b'): ('1', '2'), ('c', 'd'): ('3', '4')}

Find tuple in list with same first item and return another list

I have a list like this in Python:
[('a', 'b'), ('a', 'c'),('d','f')]
and I want join items that have same first item and result like this:
[('a', 'b', 'c'),('d','f')]
Here is one way to do it. For efficiency, we build a dict with the first value as key. We keep the values in the order in which they appear (and the tuples in their original order as well, if you use Python >= 3.7 - otherwise you will have to use a collections.OrderedDict)
def join_by_first(sequences):
out = {}
for seq in sequences:
try:
out[seq[0]].extend(seq[1:])
except KeyError:
out[seq[0]] = list(seq)
return [tuple(values) for values in out.values()]
join_by_first([('a', 'b'), ('a', 'c'),('d','f')])
# [('a', 'b', 'c'), ('d', 'f')]
You can not edit tuples - the are immuteable. You can use lists and convert all back to tuples afterward:
data = [('a', 'b'), ('a', 'c'),('d','f')]
new_data = []
for d in data # loop over your data
if new_data and new_data[-1][0] == d[0]: # if something in new_data and 1st
new_data[-1].extend(d[1:]) # ones are identical: extend
else:
new_data.append( [a for a in d] ) # not same/nothing in: add items
print(new_data) # all are lists
new_data = [tuple(x) for x in new_data]
print(new_data) # all are tuples again
Output:
[['a', 'b', 'c'], ['d', 'f']] # all are lists
[('a', 'b', 'c'), ('d', 'f')] # all are tuples again
See Immutable vs Mutable types
I feel like the simplest solution is to build a dictionary in which:
keys are the first items in the tuples
values are lists comporting all second items from the tuples
Once we have that we can then build the output list:
from collections import defaultdict
def merge(pairs):
mapping = defaultdict(list)
for k, v in pairs:
mapping[k].append(v)
return [(k, *v) for k, v in mapping.items()]
pairs = [('a', 'b'), ('a', 'c'),('d','f')]
print(merge(pairs))
This outputs:
[('a', 'b', 'c'), ('d', 'f')]
This solution is in O(n) as we only iterate two times over each item from pairs.

Sort inter-dependant elements of a dictionary

I have a dictionary like this
dict = {'name':''xyz','c1':['a','b','r','d','c'],'c2':['21','232','11','212','34']}
Here dict.c1 values and dict.c2 values are inter-dependant. That is, 'a' is related to '21', 'd' is related to '212', 'b' is related to '232'...
I have to sort c1 and c2 should get reflected accordingly. The final output should be
dict = {'name':''xyz','c1':['a','b','c','d','r'],'c2':['21','232','34','212','11']}
What is the most efficient way to do this?
This works:
d = {'name': 'xyz','c1':['a','b','r','d','c'],'c2':['21','232','11','212','34']}
s = sorted(list(zip(d['c1'], d['c2'])))
d['c1'] = [x[0] for x in s]
d['c2'] = [x[1] for x in s]
Result:
{'c1': ['a', 'b', 'c', 'd', 'r'],
'c2': ['21', '232', '34', '212', '11'],
'name': 'xyz'}
UPDATE
The call to list is not needed. Thanks to tzaman for the hint. It was a relict of putting together the solution from separate steps.
d = {'name': 'xyz','c1':['a','b','r','d','c'],'c2':['21','232','11','212','34']}
s = sorted(zip(d['c1'], d['c2']))
d['c1'] = [x[0] for x in s]
d['c2'] = [x[1] for x in s]
Your data structure does not reflect the real relationship between its elements. I would start by merging c1 and c2 into an OrderedDict. It would take care of both the relationship and the order of elements. Like this:
dict = dict(
name='xyz',
c = OrderedDict(sorted(
zip(
['a','b','r','d','c'],
['21','232','11','212','34']
)
))
)
Frankly, the most efficient way is to store the data in a form compatible with its use. In this case, you would use a table (e.g. PANDAS data frame) or simply a list:
xyz_table = [('a', '21'), ('b'. 232'), ...]
Then you simply sort the list on the first element of each tuple:
xyz_table = [('a', '21'), ('b', '232'), ('c', '34'), ('d', '212'), ('r', '11')]
xyz_sort = sorted(xyz_table, key = lambda row: row[0])
print xyz_sort
You could also look up a primer on Python sorting, such as this
Here's one way you could do it, using zip for both joining and unjoining. You can re-cast them as lists instead of tuples when you put them back into the dictionary.
a = ['a','b','r','d','c']
b = ['21','232','11','212','34']
mix = list(zip(a, b))
mix.sort()
a_new, b_new = zip(*mix)
>>> a_new
('a', 'b', 'c', 'd', 'r')
>>> b_new
('21', '232', '34', '212', '11')

Python dictionary key assign

I've created a dictionary from a tuple, but can't seem to find an answer as to how I'd switch my keys and values without editing the original tuple. This is what I have so far:
tuples = [('a', '1'), ('b', '1'), ('c', '2'), ('d', '3')]
dic = dict(tuples)
print dic
This gives the output:
{'a': '1', 'b': ''1', 'c': '2', 'd': '3'}
But I'm looking for:
{'1': 'a' 'b', '2': 'c', '3': 'd'}
Is there a simple code that could produce this?
Build a dictionary in a loop, collecting your values into lists:
result = {}
for value, key in tuples:
result.setdefault(key, []).append(value)
The dict.setdefault() method will set and return a default value if the key isn't present. Here I used it to set a default empty list value if the key is not present, so the .append(value) is applied to a list object, always.
Don't try to make this a mix of single string and multi-string list values, you'll only complicate matters down the road.
Demo:
>>> tuples = [('a', '1'), ('b', '1'), ('c', '2'), ('d', '3')]
>>> result = {}
>>> for value, key in tuples:
... result.setdefault(key, []).append(value)
...
>>> result
{'1': ['a', 'b'], '3': ['d'], '2': ['c']}
from operator import itemgetter
from itertools import groupby
first = itemgetter(0)
second = itemgetter(1)
d = dict((x, [v for _, v in y]) for x, y in groupby(sorted(tuples, key=second), key=second)
groupby groups the tuples into a new iterator of tuples, whose first element is the unique second item of each of the original, and whose second element is another iterator consisting of the corresponding first items. A quick example (pretty-printed for clarity):
>>> list(groupby(sorted(tuples, key=second), key=second)))
[('1', <itertools._grouper object at 0x10910b8d0>),
('2', <itertools._grouper object at 0x10910b790>),
('3', <itertools._grouper object at 0x10910b750>)]
The sorting by the same key used by groupby is necessary to ensure all like items are grouped together; groupby only makes one pass through the list.
The subiterators consist of tuples like ('1', 'a'), so the second value in each item is the one we want to add to the value in our new dictionary.

Categories

Resources