python running count of values in dict - python

I have a dictionary like this
d = {1:'Bob', 2:'Joe', 3:'Bob', 4:'Bill', 5:'Bill'}
I want to keep a count of how many times each name occurs as a dictionary value. So, the output should be like this:
d = {1:['Bob', 1], 2:['Joe',1], 3:['Bob', 2], 4:['Bill',1] , 5:['Bill',2]}

One way of counting the values like you want, is shown below:
from collections import Counter
d = {1:'Bob',2:'Joe',3:'Bob', 4:'Bill', 5:'Bill'}
c = Counter()
new_d = {}
for k in sorted(d.keys()):
name = d[k]
c[name] += 1;
new_d[k] = [name, c[name]]
print(new_d)
# {1: ['Bob', 1], 2: ['Joe', 1], 3: ['Bob', 2], 4: ['Bill', 1], 5: ['Bill', 2]}
Here I use Counter to keep track of occurrences of names in the input dictionary. Hope this helps. Maybe not most elegant code, but it works.

To impose an order (which a dict per se doesn't have), let's say you're going in sorted order on the keys. Then you could do -- assuming the values are hashable, as in you example...:
import collections
def enriched_by_count(somedict):
countsofar = collections.defaultdict(int)
result = {}
for k in sorted(somedict):
v = somedict[k]
countsofar[v] += 1
result[k] = [v, countsofar[v]]
return result

Without using any modules, this is the code I came up with. Maybe not as short, but I am scared of modules.
def new_dict(d):
check = [] #List for checking against
new_dict = {} #The new dictionary to be returned
for i in sorted(d.keys()): #Loop through all the dictionary items
val = d[i] #Store the dictionary item value in a variable just for clarity
check.append(val) #Add the current item to the array
new_dict[i] = [d[i], check.count(val)] #See how many of the items there are in the array
return new_dict
Use like so:
d = {1:'Bob', 2:'Joe', 3:'Bob', 4:'Bill', 5:'Bill'}
d = new_dict(d)
print d
Output:
{1: ['Bob', 1], 2: ['Joe', 1], 3: ['Bob', 2], 4: ['Bill', 1], 5: ['Bill', 2]}

Related

Create a dict based on key-value pairs without using a for loop

I still have a solution but wonder if there is a more pythonic version with in-built Python tools.
The goal would be to avoid the for loop.
Does Python offer a technique (package) to solve this?
I have 2 lists of the same length, one representing keys (with possible duplicates) and the other the values.
keys = list('ABAA')
vals = [1, 2, 1, 3]
The expected result:
{
'A': [1, 1, 3],
'B': [2]
}
My working solution (with Python 3.9):
result = {}
for k, v in zip(keys, vals):
# create new entry with empty list
if k not in result:
result[k] = []
# store the value
result[k].append(v)
print(result)
Very similar to your solution (which is great btw), but you could zip the lists and use dict.setdefault:
out = {}
for k,v in zip(keys,vals):
out.setdefault(k, []).append(v)
Output:
{'A': [1, 1, 3], 'B': [2]}
Can use this:
keys = list('ABAA')
vals = [1, 2, 1, 3]
d = {}
for key in set(keys):
d[key] = [vals[i] for i in range(len(keys)) if keys[i] == key]

Creating nested dictionaries in Python using Openpyxl

Trying to build a dictionary in Python created by looping through an Excel file using Openpyxl, where the key is the Name of a person, and the value is a list of dictionary items where each key is the Location, and the value is an array of Start and End.
Here is the Excel file:
And here is what I want:
people = {
'John':[{20:[[2,4],[3,5]]}, {21:[[2,4]]}],
'Jane':[{20:[[9,10]]},{21:[[2,4]]}]
}
Here is my current script:
my_file = openpyxl.load_workbook('Book2.xlsx', read_only=True)
ws = my_file.active
people = {}
for row in ws.iter_rows(row_offset=1):
a = row[0] # Name
b = row[1] # Date
c = row[2] # Start
d = row[3] # End
if a.value: # Only operate on rows that contain data
if a.value in people.keys(): # If name already in dict
for k, v in people.items():
for item in v:
#print(item)
for x in item:
if x == int(b.value):
print(people[k])
people[k][0][x].append([c.value,d.value])
else:
#people[k].append([c.value,d.value]) # Creates inf loop
else:
people[a.value] = [{b.value:[[c.value,d.value]]}]
Which successfully creates this:
{'John': [{20: [[2, 4], [9, 10]]}], 'Jane': [{20: [[9, 10]]}]}
But when I uncomment the line after the else: block to try to add a new Location dictionary to the initial list, it creates an infinite loop.
if x == int(b.value):
people[k][0][x].append([c.value,d.value])
else:
#people[k].append([c.value,d.value]) # Creates inf loop
I am sure there's a more Pythonic way of doing this, but pretty stuck here and looking for a nudge in the right direction. The outcome here is to analyze all of the dict items for overlapping Start/Ends per person and per location. So John's Start of 3.00 - 5.00 at location 20 overlaps with his Start/End at the same location of 2.00 - 4.00
It seems you're overthinking this; a combination of default dictionaries should do the trick.
from collections import defaultdict
person = defaultdict(dict)
for row in ws.iter_rows(min_row=2, max_col=4):
p, l, s, e = (c.value for c in row)
if p not in person:
person[p] = defaultdict(list)
person[p][l].append((s, e))
You can use the Pandas library for this. The core of this solution is a nested dictionary comprehension, each using groupby. You can, as below, use a function to take care of the nesting to aid readability / maintenance.
import pandas as pd
# define dataframe, or df = pd.read_excel('file.xlsx')
df = pd.DataFrame({'Name': ['John']*3 + ['Jane']*2,
'Location': [20, 20, 21, 20, 21],
'Start': [2.00, 3.00, 2.00, 9.00, 2.00],
'End': [4.00, 5.00, 4.00, 10.00, 4.00]})
# convert cols to integers
int_cols = ['Start', 'End']
df[int_cols] = df[int_cols].apply(pd.to_numeric, downcast='integer')
# define inner dictionary grouper and split into list of dictionaries
def loc_list(x):
d = {loc: w[int_cols].values.tolist() for loc, w in x.groupby('Location')}
return [{i: j} for i, j in d.items()]
# define outer dictionary grouper
people = {k: loc_list(v) for k, v in df.groupby('Name')}
{'Jane': [{20: [[9, 10]]}, {21: [[2, 4]]}],
'John': [{20: [[2, 4], [3, 5]]}, {21: [[2, 4]]}]}

How to match values from one list to unique values in dictionary of lists in Python

So my input values are as follows:
temp_dict1 = {'A': [1,2,3,4], 'B':[5,5,5], 'C':[6,6,7,8]}
temp_dict2 = {}
val = [5]
The list val may contain more values, but for now, it only contains one. My desired outcome is:
>>>temp_dict2
{'B':[5]}
The final dictionary needs to only have the keys for the lists that contain the item in the list val, and only unique instances of that value in the list. I've tried iterating through the two objects as follows:
for i in temp_dict1:
for j in temp_dict1[i]:
for k in val:
if k in j:
temp_dict2.setdefault(i, []).append(k)
But that just returns an argument of type 'int' is not iterable error message. Any ideas?
Changed your dictionary to cover some more cases:
temp_dict1 = {'A': [1,2,3,4], 'B':[5,5,6], 'C':[6,6,7,8]}
temp_dict2 = {}
val = [5, 6]
for item in val:
for key, val in temp_dict1.items():
if item in val:
temp_dict2.setdefault(key, []).append(item)
print(temp_dict2)
# {'B': [5, 6], 'C': [6]}
Or, the same using list comprehension (looks a bit hard to understand, not recommended).
temp_dict2 = {}
[temp_dict2.setdefault(key, []).append(item) for item in val for key, val in temp_dict1.items() if item in val]
For comparison with #KeyurPotdar's solution, this can also be achieved via collections.defaultdict:
from collections import defaultdict
temp_dict1 = {'A': [1,2,3,4], 'B':[5,5,6], 'C':[6,6,7,8]}
temp_dict2 = defaultdict(list)
val = [5, 6]
for i in val:
for k, v in temp_dict1.items():
if i in v:
temp_dict2[k].append(i)
# defaultdict(list, {'B': [5, 6], 'C': [6]})
You can try this approach:
temp_dict1 = {'A': [1,2,3,4,5,6], 'B':[5,5,5], 'C':[6,6,7,8]}
val = [5,6]
def values(dict_,val_):
default_dict={}
for i in val_:
for k,m in dict_.items():
if i in m:
if k not in default_dict:
default_dict[k]=[i]
else:
default_dict[k].append(i)
return default_dict
print(values(temp_dict1,val))
output:
{'B': [5], 'C': [6], 'A': [5, 6]}

Python. Adding multiple items to keys in a dict

I am trying to build a dict from a set of unique values to serve as the keys and a zipped list of tuples to provide the items.
set = ("a","b","c")
lst 1 =("a","a","b","b","c","d","d")
lst 2 =(1,2,3,3,4,5,6,)
zip = [("a",1),("a",2),("b",3),("b",3),("c",4),("d",5)("d",6)
dct = {"a":1,2 "b":3,3 "c":4 "d":5,6}
But I am getting:
dct = {"a":1,"b":3,"c":4,"d":5}
here is my code so far:
#make two lists
rtList = ["EVT","EVT","EVT","EVT","EVT","EVT","EVT","HIL"]
raList = ["C64G","C64R","C64O","C32G","C96G","C96R","C96O","RA96O"]
# make a set of unique codes in the first list
routes = set()
for r in rtList:
routes.add(r)
#zip the lists
RtRaList = zip(rtList,raList)
#print RtRaList
# make a dictionary with list one as the keys and list two as the values
SrvCodeDct = {}
for key, item in RtRaList:
for r in routes:
if r == key:
SrvCodeDct[r] = item
for key, item in SrvCodeDct.items():
print key, item
You don't need any of that. Just use a collections.defaultdict.
import collections
rtList = ["EVT","EVT","EVT","EVT","EVT","EVT","EVT","HIL"]
raList = ["C64G","C64R","C64O","C32G","C96G","C96R","C96O","RA96O"]
d = collections.defaultdict(list)
for k,v in zip(rtList, raList):
d[k].append(v)
You may achieve this using dict.setdefault method as:
my_dict = {}
for i, j in zip(l1, l2):
my_dict.setdefault(i, []).append(j)
which will return value of my_dict as:
>>> my_dict
{'a': [1, 2], 'c': [4], 'b': [3, 3], 'd': [5, 6]}
OR, use collections.defaultdict as mentioned by TigerhawkT3.
Issue with your code: You are not making the check for existing key. Everytime you do SrvCodeDct[r] = item, you are updating the previous value of r key with item value. In order to fix this, you have to add if condition as:
l1 = ("a","a","b","b","c","d","d")
l2 = (1,2,3,3,4,5,6,)
my_dict = {}
for i, j in zip(l1, l2):
if i in my_dict: # your `if` check
my_dict[i].append(j) # append value to existing list
else:
my_dict[i] = [j]
>>> my_dict
{'a': [1, 2], 'c': [4], 'b': [3, 3], 'd': [5, 6]}
However this code can be simplified using collections.defaultdict (as mentioned by TigerhawkT3), OR using dict.setdefault method as:
my_dict = {}
for i, j in zip(l1, l2):
my_dict.setdefault(i, []).append(j)
In dicts, all keys are unique, and each key can only have one value.
The easiest way to solve this is have the value of the dictionary be a list, as to emulate what is called a multimap. In the list, you have all the elements that is mapped-to by the key.
EDIT:
You might want to check out this PyPI package: https://pypi.python.org/pypi/multidict
Under the hood, however, it probably works as described above.
Afaik, there is nothing built-in that supports what you are after.

Combine Python dictionaries that have the same Key name

I have two separate Python List that have common key names in their respective dictionary. The second list called recordList has multiple dictionaries with the same key name that I want to append the first list clientList. Here are examples lists:
clientList = [{'client1': ['c1','f1']}, {'client2': ['c2','f2']}]
recordList = [{'client1': {'rec_1':['t1','s1']}}, {'client1': {'rec_2':['t2','s2']}}]
So the end result would be something like this so the records are now in a new list of multiple dictionaries within the clientList.
clientList = [{'client1': [['c1','f1'], [{'rec_1':['t1','s1']},{'rec_2':['t2','s2']}]]}, {'client2': [['c2','f2']]}]
Seems simple enough but I'm struggling to find a way to iterate both of these dictionaries using variables to find where they match.
When you are sure, that the key names are equal in both dictionaries:
clientlist = dict([(k, [clientList[k], recordlist[k]]) for k in clientList])
like here:
>>> a = {1:1,2:2,3:3}
>>> b = {1:11,2:12,3:13}
>>> c = dict([(k,[a[k],b[k]]) for k in a])
>>> c
{1: [1, 11], 2: [2, 12], 3: [3, 13]}
Assuming you want a list of values that correspond to each key in the two lists, try this as a start:
from pprint import pprint
clientList = [{'client1': ['c1','f1']}, {'client2': ['c2','f2']}]
recordList = [{'client1': {'rec_1':['t1','s1']}}, {'client1': {'rec_2':['t2','s2']}}]
clientList.extend(recordList)
outputList = {}
for rec in clientList:
k = rec.keys()[0]
v = rec.values()[0]
if k in outputList:
outputList[k].append(v)
else:
outputList[k] = [v,]
pprint(outputList)
It will produce this:
{'client1': [['c1', 'f1'], {'rec_1': ['t1', 's1']}, {'rec_2': ['t2', 's2']}],
'client2': [['c2', 'f2']]}
This could work but I am not sure I understand the rules of your data structure.
# join all the dicts for better lookup and update
clientDict = {}
for d in clientList:
for k, v in d.items():
clientDict[k] = clientDict.get(k, []) + v
recordDict = {}
for d in recordList:
for k, v in d.items():
recordDict[k] = recordDict.get(k, []) + [v]
for k, v in recordDict.items():
clientDict[k] = [clientDict[k]] + v
# I don't know why you need a list of one-key dicts but here it is
clientList = [dict([(k, v)]) for k, v in clientDict.items()]
With the sample data you provided this gives the result you wanted, hope it helps.

Categories

Resources