Adding Two Lists into One Dictionary (Python) - python

I have two lists, one containing a list of keys, and another containing the values to be assigned to each key, chronologically, by key.
For example;
key_list = ['cat', 'dog', 'salamander']
value_list = [1, 2, 3, 4, 5, 6, 7, 8, 9]
I'm looking to make a quick method that takes these two lists, and from it can spit out a dictionary that looks like this:
key_value_pairs = {
'cat': [1, 4, 7],
'dog': [2, 5, 8],
'salamander': [3, 6, 9]
}
Regardless of the length of the values, I'm looking for a way to just iterate through each value and amend them to a dictionary containing one entry for each item in the key_list. Any ideas?

key_value_pairs = {k: [v for v_i, v in enumerate(value_list) if v_i % len(key_list) == k_i] for k_i, k in enumerate(key_list)}
Edit: that's a fun one-liner, but it has worse time complexity than the following solution, which doesn't use any nested loops:
lists = [[] for _ in key_list]
for i, v in enumerate(value_list):
lists[i % len(key_list)].append(v)
key_value_pairs = dict(zip(keys, lists))

Related

Insert dictionary key and values to a python list

I was trying to insert key and value of my dictionary to a python list.I cant seem to figure it out how to do this.
my_dict={test1:[1,2,3,4],test2:[2,3,4,5]}
what I want is
my_list = [['test1', 1,2,3,4], ['test2', 2,3,4,5]]
I am new to python so any help would be appreciated.
This should do it
We need to iterate over the dictionary, and make a list with the key and values, note that we need to unroll the value array *value in order to append to the list
my_dict={'test1':[1,2,3,4],'test2':[2,3,4,5]}
#Iterate over key and value, and make a list from them, unrolling the value since it is a list
my_list = [[key, *value] for key, value in my_dict.items()]
print(my_list)
#[['test1', 1, 2, 3, 4], ['test2', 2, 3, 4, 5]]
Using a list comprehension
Ex:
my_dict={"test1":[1,2,3,4],"test2":[2,3,4,5]}
my_list = [[k] +v for k, v in my_dict.items()]
print(my_list)
Output:
[['test1', 1, 2, 3, 4], ['test2', 2, 3, 4, 5]]
The other solutions use list comprehensions which may be too complicated for someone who is new to python, so this is a solution without list comprehension.
my_dict={"test1":[1,2,3,4],"test2":[2,3,4,5]}
new_list = []
for key, value in my_dict.items():
print(key, value)
temp = []
temp.append(str(key))
for a in value:
temp.append(a)
new_list.append(temp)
print(new_list)
# [['test1', 1, 2, 3, 4], ['test2', 2, 3, 4, 5]]
Here's a version without the list comprehension, I remember it took me a couple months to understand the syntax when I was new to python.
my_dict={'test1':[1,2,3,4],'test2':[2,3,4,5]}
my_list = []
for key, value in my_dict.items():
my_list.append([key, *value]) # the '*value' bit means all elements of the 'value' variable
print(my_list)
FOMO:
my_dict={'test1':[1,2,3,4],'test2':[2,3,4,5]}
x = lambda a:[a[0],*a[1]]; print([x(i) for i in my_dict.items()])
#[['test1', 1, 2, 3, 4], ['test2', 2, 3, 4, 5]]

Can a loop reference a list without naming it, within a global frame?

I have been tasked to group a list by frequency. This is a very common question on SOF and so far the forum has been very educational. However, of all the examples given, only one follows these perimeters:
Sort the given iterable so that its elements end up in the decreasing frequency order.
If two elements have the same frequency, they should end up in the same order as the first appearance in the iterable.
Using these two lists:
[4, 6, 2, 2, 6, 4, 4, 4]
[17, 99, 42]
The following common codes given as solutions to this question have failed.
from collections import Counter
freq = Counter(items)
# Ex 1
# The items dont stay grouped in the final list :(
sorted(items, key = items.count, reverse=True)
sorted(items, key=lambda x: -freq[x])
[4, 4, 4, 4, 6, 2, 2, 6]
# Ex 2
# The order that the items appear in the list gets rearranged :(
sorted(sorted(items), key=freq.get, reverse=True)
[4, 4, 4, 4, 2, 2, 6, 6]
# Ex 3
# With a list of integers, after the quantity gets sorted,
# the int value gets sorted :(
sorted(items, key=lambda x: (freq[x], x), reverse=True)
[99, 42, 17]
I did find a solution that works great though:
s_list = sorted(freq, key=freq.get, reverse=True)
new_list = []
for num in s_list:
for rep in range(freq[num]):
new_list.append(num)
print(new_list)
I can't figure out how the second loop references the number of occurrences though.
I ran the process through pythontutor to visualize it and the code seems to simply know that there are four "4", two "6" and two "2" in the 'items' list. The only solution I can think of is that python can reference a list in a global frame without it being named. Or perhaps being able to utilize the value from the "freq" dictionary. Is this correct?
referenced thread:
Sort list by frequency in python
Yes, the values of freq are the ones making the second loop work.
freq is a Counter:
It is an unordered collection where elements are stored as dictionary keys and their counts are stored as dictionary values.
In other words, freq is a dictionary which keys are the unique elements of items mapped to the amount of times they appeared in items.
And to illustrate your example:
>>> from collections import Counter
>>> items = [4, 6, 2, 2, 6, 4, 4, 4]
>>> freq = Counter(items)
>>> freq
Counter({4: 4, 6: 2, 2: 2})
So when range(freq[num]) is iterated over in your second loop, all it does is iterating over the amount of times num appeared in items.
Edit 2019-02-13: Additional information and example for Python Tutor
It looks like Python Tutor represents simple build-in types (integers, strings, ...) as-is, and not as "objects" in their own cell.
You can see the references clearly if you use new objects instead of integer. For instance, if you were to wrap the integer such as:
from collections import Counter
class MyIntWrapper:
def __init__(self, value):
self.value = value
items = [4, 6, 2, 2, 6, 4, 4, 4]
items_wrapped = [MyIntWrapper(item) for item in items]
freq = Counter(items_wrapped)
s_list = sorted(freq, key=freq.get, reverse=True)
new_list = []
for num in s_list:
for rep in range(freq[num]):
new_list.append(num)

Get dictionary mapping values to reference ID

I have a list of numpy arrays like the following:
list_list = [np.array([53, 5, 2, 5, 5, 2, 1, 5, 9]), np.array([6, 4, 1,2, 53, 23, 1, 4])]
and a list of IDs for each array above:
ID = [6, 2]
How can I get a dictionary that for each unique value in list_list, I get a list of the IDs which contain it?
For example, for this very simple example, I want something like:
{53: [6, 2], 5: [6], 2: [6, 2], 1: [6, 2], etc}
My actual list_list is over 1000 lists long, with each numpy array containing around 10 million value, so efficiency in the solution is key.
I know that dict(zip(ID, list_list)) will give me a dictionary corresponding an ID with all of its values, but it won't give me a value corresponding to IDs, which is what I want.
Thanks!
The best way to approach a problem like this is to break it into smaller steps. Describe these in a combination of English and pseudo-python as seems appropriate. You seem to have the right idea to get started with zip(ID, list_list). This creates the association between the two lists as we discussed in the comments.
So what next? Well, we want to build a dictionary with the values in list_list as keys. To do so, we need to iterate over the list returned by zip():
for id, list in zip(ID, list_list):
pass
And then we need to iterate over the elements of list to determine the keys of the dictionary:
for id, list in zip(ID, list_list):
for x in list:
pass
Now we need an empty dictionary to add things to:
d = {}
for id, list in zip(ID, list_list):
for x in list:
pass
Next, we need to get a list for the dictionary if it exists. If it doesn't exist, we can use an empty list instead. Then append the id to the list and put it into the dictionary:
d = {}
for id, list in zip(ID, list_list):
for x in list:
l = d.get(x, [])
l.append(id)
d[x] = l
Notice how I describe in words what to do at each step and then translate it into Python. Breaking a problem into small steps like this is a key part of developing your skills as a programmer.
We iterate the Zip(ID,list_list) and to get only the unique elements in the lis by creating a set of it.
Then we will iterate through this set and if that element is not allready present in the dictionary we add it to the dictionary if it's already present we append the id.
import numpy as np
list_list = [np.array([53, 5, 2, 5, 5, 2, 1, 5, 9]), np.array([6, 4, 1,2, 53, 23, 1, 4])]
ID = [6, 2]
dic={}
for id,lis in zip(ID,list_list):
lis=set(lis)
for ele in lis:
if ele not in dic:
dic[ele]=[id]
else:
dic[ele].append(id)
print(dic)
{1: [6, 2], 2: [6, 2], 5: [6], 9: [6], 53: [6, 2], 4: [2], 6: [2], 23: [2]}

Python - reordering items in list by moving some items to the front while keeping the rest in the same order

I am trying to reorder items in a list in a way illustrated by the following example:
Suppose the list before reordering is:
list1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
I want to implement a method called reorder_list(list, custom_order) such that:
list1 = reorder_list(list1, [3, 6, 12, 9])
print(list1)
Out: [3, 6, 9, 1, 2, 4, 5, 7, 8, 10]
Explanation: [3, 6, 12, 9] is a custom order I am specifying. 12 is not in list1 so it will be ignored. 3,6,9 are in list1, so they get moved to the front of the list and their order is the same as in [3, 6, 12, 9]. The remaining items in list1 are after 3,6,9 and in the original order.
Is there is an easier way (and a Pythonic way) than implementing the C-like loop code. For my purpose I care more about code simplicity than performance.
def reorder_list(items, early):
moved = [item for item in early if item in items]
remain = [item for item in items if item not in moved]
return moved + remain
This is really the same algorithm as Gireesh and Stephen Rauch wrote. Gireesh's version is written as it would be before list comprehensions, while Stephen's uses sets for faster lookups (but converts both input lists to sets; one should suffice) and extends with a generator expression instead of allocating a second list.
One thing of note is that we've assumed items are unique within the lists. Both in and set expect this.
00sdf0's answer uses a very different algorithm that might make sense in Haskell, with its lazy evaluation and tail call optimization, but in this case seems neither easily understood nor performant. It can be more clearly rewritten using slices:
def reorder_list(items, early):
result = list(items)
for move in reversed(early):
try:
place = result.index(move)
result = [result[place]] + result[:place] + result[place+1:]
except ValueError:
pass # this item wasn't in the list
This does allocate more lists, effectively shallow copying the list twice per moved item. Using islice instead of slice produced lazy evaluation that avoided one of those copies.
def reorder_list(list_main, custom_order):
# initializing empty list
list1 = list()
# to add values of custom list to list1 which are present in main list
for value in custom_order:
# add only the values which are present in main list
if value in list_main:
list1.append(value)
# to add remaining element of main list to list1 which are not present in list1
for value in list_main:
if value not in list1:
list1.append(value)
return list1
list1 = [1,2,3,4,5,6,7,8,9,10]
list1 = reorder_list(list1, [3,6,12,9])
print(list1)
A couple of list comprehensions should be reasonably performant for this:
Code:
def reorder_list(list_to_reorder, custom_order):
new_list = [x for x in custom_order if x in set(list_to_reorder)]
new_list.extend(x for x in list_to_reorder if x not in set(custom_order))
return new_list
Test Code:
list1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
print(reorder_list(list1, [9, 6, 3, 12]))
Results:
[9, 6, 3, 1, 2, 4, 5, 7, 8, 10]
The problem may be solved in the following way using itertools.chain and itertools.islice.
from itertools import chain, islice
lst = [1,2,3,4,5,6,7,8,9,10]
items_to_move = [9,6,3,12]
# move index i to front of list
def front(seq, i):
item = islice(seq, i, i+1)
start = islice(seq, 0, i, None)
end = islice(seq, i+1, None)
return list(chain(item,start,end))
for item in reversed(items_to_move):
if item in lst:
lst = front(lst, lst.index(item))
Output:
[9, 6, 3, 1, 2, 4, 5, 7, 8, 10]

Quickest way to dedupe list in dict [duplicate]

This question already has answers here:
How do I remove duplicates from a list, while preserving order?
(31 answers)
Closed 7 years ago.
I have a dict containing lists and need a fast way to dedupe the lists.
I know how to dedupe a list in isolation using the set() function, but in this case I want a fast way of iterating through the dict, deduping each list on the way.
hello = {'test1':[2,3,4,2,2,5,6], 'test2':[5,5,8,4,3,3,8,9]}
I'd like it to appear like;
hello = {'test1':[2,3,4,5,6], 'test2':[5,8,4,3,9]}
Though I don't necessarily need to have the original order of the lists preserved.
I've tried using a set like this, but it's not quite correct (it's not iterating properly and I'm losing the first key)
for key, value in hello.items(): goodbye = {key: set(value)}
>>> goodbye
{'test2': set([8, 9, 3, 4, 5])}
EDIT: Following PM 2Ring's comment below, I'm now populating the dict differently to avoid duplicates in the first place. Previously I was using lists, but using sets prevents dupes to be appended by default;
>>> my_numbers = {}
>>> my_numbers['first'] = [1,2,2,2,6,5]
>>> from collections import defaultdict
>>> final_list = defaultdict(set)
>>> for n in my_numbers['first']: final_list['test_first'].add(n)
...
>>> final_list['test_first']
set([1, 2, 5, 6])
As you can see, the final output is a deduped set, as required.
It's not iterating wrong, you're just assigning goodbye as a new dict each time. You need to assign as an empty dict then assign the values to keys in each iteration.
goodbye = {}
for key, value in hello.items(): goodbye[key] = set(value)
>>> goodbye
{'test1': set([2, 3, 4, 5, 6]), 'test2': set([8, 9, 3, 4, 5])}
Also since sets don't preserve order, if you do want to preserve order it's best to make a simple iterating function that will return a new list that skips over already added values.
def uniqueList(li):
newList = []
for x in li:
if x not in newList:
newList.append(x)
return newList
goodbye = {}
for key, value in hello.items(): goodbye[key] = uniqueList(value)
>>> goodbye
{'test1': [2, 3, 4, 5, 6], 'test2': [5, 8, 4, 3, 9]}
You can use a list comprehension with a deduplicate function that preserves the order:
def deduplicate(seq):
seen = set()
seen_add = seen.add
return [ x for x in seq if not (x in seen or seen_add(x))]
{key: deduplicate(value) for key, value in hello.items()}
>>>hello = {'test1':[2,3,4,2,2,5,6], 'test2':[5,5,8,4,3,3,8,9]}
>>>for key,value in hello.iteritems():
hello[key] = list(set(value))
>>>hello
{'test1': [2, 3, 4, 5, 6], 'test2': [8, 9, 3, 4, 5]}
This is a more verbose way of doing it, which preserves order and works in all Python versions:
for key in hello:
s = set()
l = []
for subval in hello[key]:
if subval not in s:
l.append(subval)
s.add(subval)
hello[key] = l
my_list = [1,2,2,2,3,4,5,6,7,7,7,7,7,8,9,10]
seen = set()
print list(filter(lambda x:x not in seen and not seen.add(x),my_list))

Categories