how to get all the keys in a 2d dict python - python

I have a dictionary of form:
d = {123:{2:1,3:1}, 124:{3:1}, 125:{2:1},126:{1:1}}
So, lets look into 2nd degree keys..
123--> 2,3
124--> 3
125--> 2
126--> 1
So total number of unique 2nd order keys are:
1,2,3
Now, i want to modify this dict as
d = {123:{1:0,2:1,3:1}, 124:{1:0,2:0,3:1}, 125:{1:0,2:1,3:0},126:{1:1,2:0,3:0}}
So basically all the 2nd order keys absent in a particular 2d dict.. add that key with value 0.
What is the pythonic way to do this?
Thanks

keyset = set()
for k in d:
keyset.update(d[k])
for k in d:
for kk in keyset:
d[k].setdefault(kk, 0)

In [25]: d = {123:{2:1,3:1}, 124:{3:1}, 125:{2:1},126:{1:1}}
In [26]: se=set(y for x in d for y in d[x])
In [27]: for x in d:
foo=se.difference(d[x])
d[x].update(dict(zip(foo,[0]*len(foo))))
....:
....:
In [30]: d
Out[30]:
{123: {1: 0, 2: 1, 3: 1},
124: {1: 0, 2: 0, 3: 1},
125: {1: 0, 2: 1, 3: 0},
126: {1: 1, 2: 0, 3: 0}}
here use set difference to get the missing keys and then update() the dict:
In [39]: for x in d:
foo=se.difference(d[x])
print foo # missing keys per dict
set([1])
set([1, 2])
set([1, 3])
set([2, 3])

I like the solution of Ashwini Chaudhary.
I edited it to incorporate all the suggestions in the comments with other minor changes for it to look how I would prefer it:
Edited (incorporates the suggestion of Steven Rumbalski to this answer).
all_second_keys = set(key for value in d.itervalues() for key in value)
for value in d.itervalues():
value.update((key,0) for key in all_second_keys if key not in value)

import operator
second_order_keys = reduce(operator.__or__,
(set(v.iterkeys()) for v in d.itervalues()))
for v in d.itervalues():
for k in second_order_keys:
v.setdefault(k, 0)
Or, in Python 3:
from functools import reduce
import operator
second_order_keys = reduce(operator.__or__,
(v.keys() for v in d.values()))
for v in d.values():
for k in second_order_keys:
v.setdefault(k, 0)

Related

Sort a dict by values, given that a key contains many values

I am trying to sort a dict by values, where each of my keys have many values.
I know It is not possible to sort a dictionary, only to get a representation of a dictionary that is sorted. Dictionaries are inherently orderless.
I tried this solutions, from another post but it doesn't seem to work for my case :
x = {1: 2, 3: 4, 4: 3, 2: 1, 0: 0}
{k: v for k, v in sorted(x.items(), key=lambda item: item[1])}
{0: 0, 2: 1, 1: 2, 4: 3, 3: 4}
or
dict(sorted(x.items(), key=lambda item: item[1]))
{0: 0, 2: 1, 1: 2, 4: 3, 3: 4}
from this post
How do I sort a dictionary by value?
Also, I tried :
sorted(x, key=lambda x: x['key'])
But, there my dict is a string and it doesn't work and It would not be the best option if I want to apply it in every keys, i would have to repeat the process for each of them.
Here is a sample of my dict :
{
'/Users/01':
['V01File12.txt',
'V01File01.txt',
'V01File18.txt',
'V01File15.txt',
'V01File02.txt',
'V01File11.txt' ] ,
'/Users/02':
['V02File12.txt',
'V02File01.txt',
'V02File18.txt',
'V02File15.txt',
'V02File02.txt',
'V02File11.txt' ]
}
and so on ...
the expected output would be :
{'/Users/01':
['V01File01.txt',
'V01File02.txt',
'V01File11.txt',
'V01File12.txt',
'V01File15.txt',
'V01File18.txt' ] ,
'/Users/02':
['V02File01.txt',
'V02File02.txt',
'V02File11.txt',
'V02File12.txt',
'V02File15.txt',
'V02File18.txt' ]
}
So you're not trying to sort the dict, you're trying to sort the lists in it. I would just use a comprehension:
data = {k, sorted(v) for k, v in data.items()}
Or just alter the dict in place:
for v in data.values():
v.sort()
If you want to sort the list inside each dictionary:
a ={
'/Users/01':
['V01File12.txt',
'V01File01.txt',
'V01File18.txt',
'V01File15.txt',
'V01File02.txt',
'V01File11.txt' ] ,
'/Users/02':
['V02File12.txt',
'V02File01.txt',
'V02File18.txt',
'V02File15.txt',
'V02File02.txt',
'V02File11.txt' ]
for i in a:
a[i] = sorted(a[i])
The best solution is the solution of #StevenRumblaski:
for b in a.values(): b.sort()

Dictionary with values of empty list

I want to create a dictionary with keys of 1, 2, 3, 4
and each value to the key is [].
n = [1,2,3,4]
d = dict.fromkeys(n, [])
d[1].append(777)
print(d)
--> {1: [777], 2: [777], 3: [777], 4: [777]}
Why does this happen? Why is this not {1: [777], 2: [], 3: [], 4: []} ?
The list that you use as values in the second step all point to the same memory. So when you update one of the values, all of them update.
You can check that by using the following -
n = [1,2,3,4]
d = dict.fromkeys(n, [])
d[1] is d[2] #share memory
#True
So, one way you can instead do the following -
n = [1,2,3,4]
d = {k:[] for k in n}
d[1] is d[2]
#False
Then you can set then -
d[1].append(777)
{1: [777], 2: [], 3: [], 4: []}
A better way to do this is to use collections.defaultdict. This allows you to create a dictionary where values are list objects by default using defaultdict(list). Its scalable as you can choose which datatype you need your dictionary values to be.
from collections import defaultdict
n = [1,2,3,4]
d = defaultdict(list)
d[1].append(777)

Array access to python dictionary [duplicate]

I have a dictionary, and would like to pass a part of it to a function, that part being given by a list (or tuple) of keys. Like so:
# the dictionary
d = {1:2, 3:4, 5:6, 7:8}
# the subset of keys I'm interested in
l = (1,5)
Now, ideally I'd like to be able to do this:
>>> d[l]
{1:2, 5:6}
... but that's not working, since it will look for a key matching the tuple (1,5), the same as d[1,5].
d{1,5} isn't even valid Python (as far as I can tell ...), though it might be handy: The curly braces suggest an unordered set or a dictionary, so returning a dictionary containing the specified keys would look very plausible to me.
d[{1,5}] would also make sense ("here's a set of keys, give me the matching items"), and {1, 5} is an unhashable set, so there can't be a key that matches it -- but of course it throws an error, too.
I know I can do this:
>>> dict([(key, value) for key,value in d.iteritems() if key in l])
{1: 2, 5: 6}
or this:
>>> dict([(key, d[key]) for key in l])
which is more compact
... but I feel there must be a "better" way of doing this. Am I missing a more elegant solution?
(I'm using Python 2.7)
On Python 3 you can use the itertools islice to slice the dict.items() iterator
import itertools
d = {1: 2, 3: 4, 5: 6}
dict(itertools.islice(d.items(), 2))
{1: 2, 3: 4}
Note: this solution does not take into account specific keys. It slices by internal ordering of d, which in Python 3.7+ is guaranteed to be insertion-ordered.
You should be iterating over the tuple and checking if the key is in the dict not the other way around, if you don't check if the key exists and it is not in the dict you are going to get a key error:
print({k:d[k] for k in l if k in d})
Some timings:
{k:d[k] for k in set(d).intersection(l)}
In [22]: %%timeit
l = xrange(100000)
{k:d[k] for k in l}
....:
100 loops, best of 3: 11.5 ms per loop
In [23]: %%timeit
l = xrange(100000)
{k:d[k] for k in set(d).intersection(l)}
....:
10 loops, best of 3: 20.4 ms per loop
In [24]: %%timeit
l = xrange(100000)
l = set(l)
{key: d[key] for key in d.viewkeys() & l}
....:
10 loops, best of 3: 24.7 ms per
In [25]: %%timeit
l = xrange(100000)
{k:d[k] for k in l if k in d}
....:
100 loops, best of 3: 17.9 ms per loop
I don't see how {k:d[k] for k in l} is not readable or elegant and if all elements are in d then it is pretty efficient.
To slice a dictionary, Convert it to a list of tuples using d.items(), slice the list and create a dictionary out of it.
Here.
d = {1:2, 3:4, 5:6, 7:8}
To get the first 2 items
first_two = dict(list(d.items())[:2])
first_two
{1: 2, 3: 4}
Use a set to intersect on the dict.viewkeys() dictionary view:
l = {1, 5}
{key: d[key] for key in d.viewkeys() & l}
This is Python 2 syntax, in Python 3 use d.keys().
This still uses a loop, but at least the dictionary comprehension is a lot more readable. Using set intersections is very efficient, even if d or l is large.
Demo:
>>> d = {1:2, 3:4, 5:6, 7:8}
>>> l = {1, 5}
>>> {key: d[key] for key in d.viewkeys() & l}
{1: 2, 5: 6}
Write a dict subclass that accepts a list of keys as an "item" and returns a "slice" of the dictionary:
class SliceableDict(dict):
default = None
def __getitem__(self, key):
if isinstance(key, list): # use one return statement below
# uses default value if a key does not exist
return {k: self.get(k, self.default) for k in key}
# raises KeyError if a key does not exist
return {k: self[k] for k in key}
# omits key if it does not exist
return {k: self[k] for k in key if k in self}
return dict.get(self, key)
Usage:
d = SliceableDict({1:2, 3:4, 5:6, 7:8})
d[[1, 5]] # {1: 2, 5: 6}
Or if you want to use a separate method for this type of access, you can use * to accept any number of arguments:
class SliceableDict(dict):
def slice(self, *keys):
return {k: self[k] for k in keys}
# or one of the others from the first example
d = SliceableDict({1:2, 3:4, 5:6, 7:8})
d.slice(1, 5) # {1: 2, 5: 6}
keys = 1, 5
d.slice(*keys) # same
set intersection and dict comprehension can be used here
# the dictionary
d = {1:2, 3:4, 5:6, 7:8}
# the subset of keys I'm interested in
l = (1,5)
>>>{key:d[key] for key in set(l) & set(d)}
{1: 2, 5: 6}
the dictionary
d = {1:2, 3:4, 5:6, 7:8}
the subset of keys I'm interested in
l = (1,5)
answer
{key: d[key] for key in l}
Another option is to convert the dictionary into a pandas Series object and then locating the specified indexes:
>>> d = {1:2, 3:4, 5:6, 7:8}
>>> l = [1,5]
>>> import pandas as pd
>>> pd.Series(d).loc[l].to_dict()
{1: 2, 5: 6}
My case is probably relatively uncommon, but, I'm posting it here nonetheless in case it helps someone (though not OP directly).
I came across this question searching how to slice a dictionary that had item counts. Basically I had a dictionary where the keys were letters, and the values were the number of times the letter appeared (i.e. abababc --> {'a': 3, 'b': 3, 'c': 1} I wanted to 'slice' the dictionary so that I could return the most common n keys.
It turns out that this is exactly what a Collections Counter object is for, and instead of needing to 'slice' my dictionary, I could easily just convert it to a collections.Counter and then call most_common(n): https://docs.python.org/3/library/collections.html#collections.Counter.most_common
You can do slicing of the dictionary with the help of the module dictionarify
This is the link for documentation-https://github.com/suryavenom/Flexi/blob/main/README.md.
Installation -
pip install dictionarify

python iterate through an array and access the same value in a dictionary

I have a dictionary that consists of numbers and their value
dict = {1:5, 2:5, 3:5}
I have an array with some numbers
arr = [1,2]
What I want to do is:
iterate through the dict and the array
where the dictionary value is equal to the number in the array, set the dictionary value to zero
any value in the dictionary for which there isn't a value in the array matching it, add 1
so in the above example, I should end up with
arr = [1,2]
dict = {1:0, 2:0, 3:6}
The bit I am getting stuck on is creating a variable from the array value and accessing that particular number in the dictionary - using dict[i] for example
arr = [1,2]
data = {1:0, 2:0, 3:6} # don't call it dict because it shadow build-in class
unique = set(arr) # speed up search in case if arr is big
# readable
for k, v in data.items():
if k in unique:
data[k] = 0
else:
data[k] += 1
# oneliner
data = {k: (0 if k in unique else v + 1) for v, k in data.items()}
Additional example:
for a, b, c in [(1,2,3), (4,5,6)]:
print('-',a,b,c)
# will print:
# - 1 2 3
# - 4 5 6
You just need a dict-comprehension that will re-built your dictionary with an if condition for the value part.
my_dict = {1:5, 2:5, 3:5}
arr = [1,2]
my_dict = {k: (0 if k in arr else v+1) for k, v in my_dict.items()}
print(my_dict) # {1: 0, 2: 0, 3: 6}
Note that I have re-named the dictionary from dict to my_dict. That is because by using dict you are overwriting the Python built-in called dict. And you do not want to do that.
Theirs always the dict(map()) approach, which rebuilds a new dictionary with new values to each of the keys:
>>> d = {1:5, 2:5, 3:5}
>>> arr = {1, 2}
>>> dict(map(lambda x: (x[0], 0) if x[0] in arr else (x[0], x[1]+1), d.items()))
{1: 0, 2: 0, 3: 6}
This works because wrapping dict() will automatically convert mapped 2-tuples to a dictionary.
Also you should not use dict as a variable name, since it shadows the builtin dict.
Just use .update method :
dict_1 = {1:5, 2:5, 3:5}
arr = [1,2]
for i in dict_1:
if i in arr:
dict_1.update({i:0})
else:
dict_1.update({i:dict_1.get(i)+1})
print(dict_1)
output:
{1: 0, 2: 0, 3: 6}
P.S : don't use dict as variable

int values in dict appending as list

I have a for loop which is going through multiple dictionaries and adding the values under common keys. The input dictionary has keys that are strings and values that are int's. For some reason its adding the values as lists of one value (e.g. {"01":[12],[44]}). I want it to add the int on its own but cant get that working for some reason. I'm using the code below, is there something i am missing ?
dw = defaultdict()
dw = {}
for key, value in listb.items():
dw[key].append(value)
If you want to forgo all good practice and not use defaultdict(list), you can use setdefault and call it every single time you choose to add a value. This is inefficient and not idiomatic, but it will work.
In [1]: from collections import defaultdict
In [2]: a = defaultdict(list)
In [3]: b = {}
In [4]: a[1].append(1)
In [5]: b.setdefault(1, []).append(1)
In [6]: a
Out[6]: defaultdict(list, {1: [1]})
In [7]: b
Out[7]: {1: [1]}
In [8]:
As long as the values in the dicts are ints (not lists):
dw = {}
for key, value in listb.items():
try: # Key exists in dictionary and value is a list
dw[key].append(value)
except KeyError: # Key does not yet exist in dictionary
dw[key] = value
except AttributeError: # Key exist in dictionary and value is not a list
dw[key] = [dw[key], value]
If you mean to add key/value pairs to the dictionary (and not append to an array), it's:
for key, value in listb.items():
dw[key] = value
EDIT: or is it something like this you're after?
listb = {'1': 3, '2': 5}
dw = {'1': 5, '2': 9}
for key, value in listb.items():
if key not in dw.keys():
dw[key] = []
else:
dw[key] = [dw[key]]
dw[key].append(value)
which gives dw = {'2': [9, 5], '1': [5, 3]}
If you have a list like listb = [{'a': 1, 'b': 2}, {'a': 3, 'b': 4, 'c': 5}, {'b': 1}], you can try this:
dw = {}
for d in listb:
for k, v in d.items():
if k in dw:
if isinstance(dw[k], list):
dw[k].append(v)
elif isinstance(dw[k], int):
dw[k] = [dw[k], v]
else:
dw[k] = v
print(dw)
{'a': [1, 3], 'b': [2, 4, 1], 'c': 5}
>>>

Categories

Resources