Python: Renumerate elements in multiple lists - python

Suppose I have a dictionary with lists as follows:
{0: [31, 32, 58, 59], 1: [31, 32, 12, 13, 37, 38], 2: [12, 13]}
I am trying to obtain the following one from it:
{0: [1, 2, 3, 4], 1: [1, 2, 5, 6, 7, 8], 2: [5, 6]}
So I renumerate all the entries in order of occurence but skipping those that were already renumerated.
What I have now is a bunch of for loops going back and forth which works, but doesn't look good at all, could anyone please tell me the way it should be done in Python 2.7?
Thank you

import operator
data = {0: [31, 32, 58, 59], 1: [31, 32, 12, 13, 37, 38], 2: [12, 13]}
# the accumulator is the new dict with renumbered values combined with a list of renumbered numbers so far
# item is a (key, value) element out of the original dict
def reductor(acc, item):
(out, renumbered) = acc
(key, values) = item
def remapper(v):
try:
x = renumbered.index(v)
except ValueError:
x = len(renumbered)
renumbered.append(v)
return x
# transform current values to renumbered values
out[key] = map(remapper, values)
# return output and updated list of renumbered values
return (out, renumbered)
# now reduce the original data
print reduce(reductor, sorted(data.iteritems(), key=operator.itemgetter(0)), ({}, []))

If you're not worried about memory or speed you can use an intermediate dictionary to map the new values:
a = {0: [31, 32, 58, 59], 1: [31, 32, 12, 13, 37, 38], 2: [12, 13]}
b = {}
c = {}
for key in sorted(a.keys()):
c[key] = [b.setdefault(val, len(b)+1) for val in a[key]]

Just use a function like this:
def renumerate(data):
ids = {}
def getid(val):
if val not in ids:
ids[val] = len(ids) + 1
return ids[val]
return {k : map(getid, data[k]) for k in sorted(data.keys())}
Example
>>> data = {0: [31, 32, 58, 59], 1: [31, 32, 12, 13, 37, 38], 2: [12, 13]}
>>> print renumerate(data)
{0: [1, 2, 3, 4], 1: [1, 2, 5, 6, 7, 8], 2: [5, 6]}

data = {0: [31, 32, 58, 59], 1: [31, 32, 12, 13, 37, 38], 2: [12, 13]}
from collections import defaultdict
numbered = defaultdict(lambda: len(numbered)+1)
result = {key: [numbered[v] for v in val] for key, val in sorted(data.iteritems(), key=lambda item: item[0])}
print result

Related

Create a dictionary from a list of key and multi list of values in Python

I have a list of key:
list_date = ["MON", "TUE", "WED", "THU","FRI"]
I have many lists of values that created by codes below:
list_value = list()
for i in list(range(5, 70, 14)):
list_value.append(list(range(i, i+10, 3)))
Rules created that:
first number is 5, a list contains 4 items has value equal x = x + 3, and so on [5, 8, 11,1 4]
the first number of the second list equal: x = 5 + 14, and value inside still as above x = x +3
[[5, 8, 11, 14], [19, 22, 25, 28], [33, 36, 39, 42], [47, 50, 53, 56], [61, 64, 67, 70]]
I expect to obtain a dict like this:
collections = {"MON":[5, 8, 11, 14], "TUE" :[19, 22, 25, 28], "WED":[33, 36, 39, 42], "THU":[47, 50, 53, 56], "FRI":[61, 64, 67, 70]}
Then, I used:
zip_iterator = zip(list_date, list_value)
collections = dict(zip_iterator)
To get my expected result.
I tried another way like using lambda function
for i in list(range(5, 70, 14)):
list_value.append(list(range(i,i+10,3)))
couple_start_end[lambda x: x in list_date] = list(range(i, i + 10, 3))
And the output is:
{<function <lambda> at 0x000001BF7F0711F0>: [5, 8, 11, 14], <function <lambda> at 0x000001BF7F071310>: [19, 22, 25, 28], <function <lambda> at 0x000001BF7F071280>: [33, 36, 39, 42], <function <lambda> at 0x000001BF7F0710D0>: [47, 50, 53, 56], <function <lambda> at 0x000001BF7F0890D0>: [61, 64, 67, 70]}
I want to ask there is any better solution to create lists of values with the rules above? and create the dictionary collections without using the zip method?
Thank you so much for your attention and participation.
Sure, you can use enumerate but I wouldn't say it is in anyway better or worse than the zip based solution:
collections = {}
for idx, key in enumerate(list_keys):
collections[key] = list_value[idx]
print(collections)
Output:
{'MON': [5, 8, 11, 14], 'TUE': [19, 22, 25, 28], 'WED': [33, 36, 39, 42], 'THU': [47, 50, 53, 56], 'FRI': [61, 64, 67, 70]}
Further, you don't need to create the value list separately, you can create the dictionary as you go along:
list_keys = ["MON", "TUE", "WED", "THU","FRI"]
collections = {}
for idx, start in enumerate(range(5, 70, 14)):
collections[list_keys[idx]] = [i for i in range(start, start+10, 3)]
print(collections)

Accessing elements of a list within a dictionary component wise

Let's say I have a dictionary (MxN = 4x3 matrix) that has keys of integers and values of lists as follows:
d={1:[10,11,12], 2:[13,14,15], 3:[16,17,18], 4:[19,20,21]}
I am trying to get a list that looks like this:
new_list_1 = [10,13,16,19]
In this case, I am trying to get the first element of each value (list) in the dictionary and put it into a new list. However, I would also like to do this for other indexes as well such as:
new_list_2 = [11,14,17,20]
or
new_list_3 = [12,15,18,21]
Once I have these lists, ideally I would like to them in a new dictionary of dimension NxM :
d_new = {0:new_list_1, 1:new_list_2, 2:new_list_3}
Thanks!
You can use
new_list_1 = [v[0] for v in d.values()]
new_list_2 = [v[1] for v in d.values()]
new_list_3 = [v[2] for v in d.values()]
then use
d_new = {0:new_list_1, 1:new_list_2, 2:new_list_3}
Value of d_new :
{0: [10, 13, 16, 19], 1: [11, 14, 17, 20], 2: [12, 15, 18, 21]}
Alternatively:
d_new = {i:[v[i] for v in d.values()] for i in range(0,3)}
Would also do the same
convert your dict values to list of list (2d matrix) and then use transpose of that matrix and then make the result according to your needs.
res = {i: list(k) for i ,k in enumerate(list(zip(*d.values())))}
print(res)
# {0: [10, 13, 16, 19], 1: [11, 14, 17, 20], 2: [12, 15, 18, 21]}
With numpy you can:
import numpy as np
d = {
1: [10, 11, 12],
2: [13, 14, 15],
3: [16, 17, 18],
4: [19, 20, 21],
}
a = np.array(list(d.values()))
a = a.T
Output:
array([[10, 13, 16, 19],
[11, 14, 17, 20],
[12, 15, 18, 21]])
Back to dict:
d = {n+1: l for n, l in enumerate(a.tolist())}
Output:
{1: [10, 13, 16, 19], 2: [11, 14, 17, 20], 3: [12, 15, 18, 21]}
You can use pandas as well:
import pandas as pd
d_new = pd.DataFrame.from_dict(d, orient = 'index').to_dict('list')
Hope this helps!
You can do it simply like so:
d_new = {}
for i in range(len(list(d.values())[0])):
d_new[i] = [x[i] for x in d.values()]
Hope it helps :)
Easy to understand:
d={1:[10,11,12], 2:[13,14,15], 3:[16,17,18], 4:[19,20,21]}
d_new = {}
for i in range(len(d[1])):
l = []
for v in d.values():
l.append(v[i])
d_new[i] = l
print(d_new)
output:
{0: [10, 13, 16, 19], 1: [11, 14, 17, 20], 2: [12, 15, 18, 21]}
Code :
lista_1 = []
lista_2 = []
lista_3 = []
d_new = {}
for i in d.values():
for h, j in enumerate(i):
if h == 0:
lista_1.append(j)
d_new[h] = lista_1
elif h == 1:
lista_2.append(j)
d_new[h] = lista_2
else:
lista_3.append(j)
d_new[h] = lista_3
Output :
{0: [10, 13, 16, 19], 1: [11, 14, 17, 20], 2: [12, 15, 18, 21]}
Maybe this can help you, it is for beginners' understanding!

Count total number of occurrences of given list of integers in another

How do I count the number of times the same integer occurs?
My code so far:
def searchAlgorithm (target, array):
i = 0 #iterating through elements of target list
q = 0 #iterating through lists sublists via indexes
while q < 4:
x = 0 #counting number of matches
for i in target:
if i in array[q]:
x += 1
else:
x == 0
print(x)
q += 1
a = [8, 12, 14, 26, 27, 28]
b = [[4, 12, 17, 26, 30, 45], [8, 12, 19, 24, 33, 47], [3, 10, 14, 31, 39, 41], [4, 12, 14, 26, 30, 45]]
searchAlgorithm(a, b)
The output of this is:
2
2
1
3
What I want to achieve is counting the number of times '1', '2' '3' matches occurs.
I have tried:
v = 0
if searchAlgorithm(a, b) == 2:
v += 1
print(v)
But that results in 0
You can use intersection of sets to find elements that are common in both lists. Then you can get the length of the sets. Here is how it looks:
num_common_elements = (len(set(a).intersection(i)) for i in b)
You can then iterate over the generator num_common_elements to use the values. Or you can cast it to a list to see the results:
print(list(num_common_elements))
[Out]: [2, 2, 1, 3]
If you want to implement the intersection functionality yourself, you can use the sum method to implement your own version. This is equivalent to doing len(set(x).intersection(set(y))
sum(i in y for i in x)
This works because it generates values such as [True, False, False, True, True] representing where the values in the first list are present in the second list. The sum method then treats the Trues as 1s and Falses as 0s, thus giving you the size of the intersection set
This is based on what I understand from your question. Probably you are looking for this:
from collections import Counter
def searchAlgorithm (target, array):
i = 0 #iterating through elements of target list
q = 0 #iterating through lists sublists via indexes
lst = []
while q < 4:
x = 0 #counting number of matches
for i in target:
if i in array[q]:
x += 1
else:
x == 0
lst.append(x)
q += 1
print(Counter(lst))
a = [8, 12, 14, 26, 27, 28]
b = [[4, 12, 17, 26, 30, 45], [8, 12, 19, 24, 33, 47], [3, 10, 14, 31, 39, 41], [4, 12, 14, 26, 30, 45]]
searchAlgorithm(a, b)
# Counter({2: 2, 1: 1, 3: 1})
Thanks to some for their helpful feedback, I have since come up a more simplified solution that does exactly what I want.
By storing the results of the matches in a list, I can then return the list out of the searchAlgorithm function and simple use .count() to count all the matches of a specific number within the list.
def searchAlgorithm (target, array):
i = 0
q = 0
results = []
while q < 4:
x = 0 #counting number of matches
for i in target:
if i in array[q]:
x += 1
else:
x == 0
results.append(x)
q += 1
return results
a = [8, 12, 14, 26, 27, 28]
b = [[4, 12, 17, 26, 30, 45], [8, 12, 19, 24, 33, 47], [3, 10, 14, 31, 39, 41], [4, 12, 14, 26, 30, 45]]
searchAlgorithm(a, b)
d2 = (searchAlgorithm(winNum, lotto).count(2))

Group consecutive integers together

Have the following code:
import sys
ints = [1,2,3,4,5,6,8,9,10,11,14,34,14,35,16,18,39,10,29,30,14,26,64,27,48,65]
ints.sort()
ints = list(set(ints))
c = {}
for i,v in enumerate(ints):
if i+1 >= len(ints):
continue
if ints[i+1] == v + 1 or ints[i-1] == v - 1:
if len(c) == 0:
c[v] = [v]
c[v].append(ints[i+1])
else:
added=False
for x,e in c.items():
last = e[-1]
if v in e:
added=True
break
if v - last == 1:
c[x].append(v)
added=True
if added==False:
c[v] = [v]
else:
if v not in c:
c[v] = [v]
print('input ', ints)
print('output ', c))
The objective:
Given a list of integers, create a dictionary that contains consecutive integers grouped together to reduce the overall length of the list.
Here is output from my current solution:
input [1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 14, 16, 18, 26, 27, 29, 30, 34, 35, 39, 48, 64, 65]
output {1: [1, 2, 3, 4, 5, 6], 8: [8, 9, 10, 11], 14: [14], 16: [16], 18: [18], 26: [26, 27], 29: [29, 30], 34: [34, 35], 39: [39], 48: [48], 64: [64]}
Conditions/constraints:
If the current integer is either a) in an existing list or b) is the last item in an existing list, we don't want to create another list for this item.
i.e. in the range 1-5 inclusive, when we get to 3, don't create a list 3,4, instead append 3 to the existing list [1,2]
My current iteration works fine, but it gets exponentially slower the bigger the list is because of the for x,e in c.items() existing list check.
How can I make this faster while still achieving the same result?
New solution (from 13 seconds to 0.03 seconds using an input list of 19,000 integers):
c = {}
i = 0
last_list = None
while i < len(ints):
cur = ints[i]
if last_list is None:
c[cur] = [cur]
last_list = c[cur]
else:
if last_list[-1] == cur-1:
last_list.append(cur)
else:
c[cur] = [cur]
last_list = c[cur]
i += 1
As you have lists of consecutive numbers, I suggest you to use range objects instead of lists:
d, head = {}, None
for x in l:
if head is None or x != d[head].stop:
head = x
d[head] = range(head, x+1)
The solution is simple if you use a for loop and just keep track of your current list. Don't forget to make a new list when you find a gap:
result = {}
cl = None
for i in ints:
if cl is None or i - 1 != cl[-1]:
cl = result.setdefault(i, [])
cl.append(i)
There is a great library called more_itertools which has a method called: consecutive_groups():
import more_itertools as mit
x = [1,2,3,4,5,6,8,9,10,11,14,34,14,35,16,18,39,10,29,30,14,26,64,27,48,65]
x = [list(j) for j in mit.consecutive_groups(sorted(list(set(x))))]
# [[1, 2, 3, 4, 5, 6], [8, 9, 10, 11], [14], [16], [18], [26, 27], [29, 30], [34, 35], [39], [48], [64, 65]]
dct_x = {i[0]: i for i in x}
print(dct_x)
Output:
{1: [1, 2, 3, 4, 5, 6], 8: [8, 9, 10, 11], 14: [14], 16: [16], 18: [18], 26: [26, 27], 29: [29, 30], 34: [34, 35], 39: [39], 48: [48], 64: [64, 65]}
One more comment, you want to sort after converting to and from a set, since sets are unordered.
One can solve this task in O(n) (linear) complexity. Just keep it simple:
integers = [1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 14, 16, 18, 26, 27, 29, 30, 34, 35, 39, 48, 64, 65]
helper = []
counter = 0
while counter < len(integers):
if not helper or helper[-1] + 1 != integers[counter]:
print('gap found', integers[counter]) # do your logic
helper.append(integers[counter])
counter += 1
The algorithm above assumes that the input list is already sorted. It gives us a huge advantage. At the same time one can sort the list of integers explicitly before running this algorithm. The total complexity of the solution will be then: O(n * log n) + O(n) which is efficiently O(n * log n). And O(n * log n) is the complexity of the sorting procedure.
I would kindly suggest to remember this extremely useful trick of using sorting before approaching a task for future usages.
Here's a simple implementation that achieves what you are after, using list slicing:
integers = [1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 14, 16, 18, 26, 27, 29, 30, 34, 35, 39, 48, 64, 65]
for i, integer in enumerate(integers):
if i == 0:
out_dict = {}
start = 0
else:
if integer != prev_integer + 1:
out_dict[integers[start]] = integers[start:i]
start = i
if i == len(integers) - 1:
out_dict[integers[start]] = integers[start:]
prev_integer = integer
>>>out_dict = {1: [1, 2, 3, 4, 5, 6], 8: [8, 9, 10, 11], 14: [14], 16: [16], 18: [18], 26: [26, 27], 29: [29, 30], 34: [34, 35], 39: [39], 48: [48], 64: [64]}
Note: The dictionary will likely not be sorted by ascending keys, as dict types are not ordered.
You can try with itertools , But i would like to try recursion :
input_dta=[1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 14, 16, 18, 26, 27, 29, 30, 34, 35, 39, 48, 64, 65]
final_=[]
def consecutives(data):
sub_final=[]
if not data:
return 0
else:
for i,j in enumerate(data):
try:
if abs(data[i]-data[i+1])==1:
sub_final.extend([data[i],data[i+1]])
else:
if sub_final:
final_.append(set(sub_final))
return consecutives(data[i+1:])
except IndexError:
pass
final_.append(set(sub_final))
consecutives(input_dta)
print(final_)
output:
[{1, 2, 3, 4, 5, 6}, {8, 9, 10, 11}, {26, 27}, {29, 30}, {34, 35}, {64, 65}]

Comparing lists with their indices and content in Python

I have a list of numbers as
N = [13, 14, 15, 25, 27, 31, 35, 36, 43]
After some calculations, for each element in N, I get the following list as the answers.
ndlist = [4, 30, 0, 42, 48, 4, 3, 42, 3]
That is, for the first index in N (which is 13), my answer is 4 in ndlist.
For some indices in N, I get the same answer in ndlist. For example, when N= 13 and 31, the answer is 4 in ndlist.
I need to find the numbers in N (13 and 31 in my example) such that they have the same answer in ndlist.
Can someone help me to that?
You can use a defaultdict and put those into a list keyed by the answer like:
Code:
N = [13, 14, 15, 25, 27, 31, 35, 36, 43]
ndlist = [4, 30, 0, 42, 48, 4, 3, 42, 3]
from collections import defaultdict
answers = defaultdict(list)
for n, answer in zip(N, ndlist):
answers[answer].append(n)
print(answers)
print([v for v in answers.values() if len(v) > 1])
Results:
defaultdict(<class 'list'>, {4: [13, 31], 30: [14],
0: [15], 42: [25, 36], 48: [27], 3: [35, 43]})
[[13, 31], [25, 36], [35, 43]]
Here is a way using only a nested list comprehension:
[N[idx] for idx, nd in enumerate(ndlist) if nd in [i for i in ndlist if ndlist.count(i)>1]]
#[13, 25, 31, 35, 36, 43]
To explain: the inner list comprehension ([i for i in ndlist if ndlist.count(i)>1]) gets all duplicate values in ndlist, and the rest of the list comprehension extracts the corresponding values in N where those values are found in ndlist

Categories

Resources