In example, if I have the following script:
import collections
lst = [['a','b'],['b','a'],['c','d'],['c','d'],['d','c']]
print([(a, b, v) for (a, b),v in collections.Counter(map(tuple,lst)).items()])
I get as output:
[('a', 'b', 1), ('b', 'a', 1), ('c', 'd', 2), ('d', 'c', 1)]
Can I adapt my code to yield the following output:
[('a', 'b', 2), ('c', 'd', 3)]
So a function that doesn't include the order of the pairs?
Use a data structure that doesn't care about order. In this case you'll need frozenset instead of a regular set because Counter requires it to be hashable. But basically it's a simple substitution of tuple in your original code for frozenset:
print([(a, b, v) for (a, b),v in collections.Counter(map(frozenset,lst)).items()])
Output:
[('a', 'b', 2), ('d', 'c', 3)]
You could just sort each element in the list before counting, like so:
import collections
lst = [['a','b'],['b','a'],['c','d'],['c','d'],['d','c']]
sorted_lst = [sorted(x) for x in lst]
print([(a, b, v) for (a, b),v in collections.Counter(map(tuple,sorted_lst)).items()])
Output:
[('a', 'b', 2), ('c', 'd', 3)]
Sorting the list before you get collections of it solves the problem.
import collections
lst = [['a','b'],['b','a'],['c','d'],['c','d'],['d','c']]
sort_list = sorted(x) for x in lst
print([(a, b, v) for (a, b),v in collections.Counter(map(tuple,sort_list)).items()])
You could sort the values of the key a,b and use groupby in itertools and then sum all the elements in the group.
import itertools as it
lst = [['a','b'],['b','a'],['c','d'],['c','d'],['d','c']]
output = [(*group,sum(1 for i in elements)) for group,elements in it.groupby(lst,key=lambda x:sorted(x))]
print(output)
OUTPUT
[('a', 'b', 2), ('c', 'd', 3)]
Related
I am trying to get an output list from nested list based on nested indices.
Input:
list_a = [(a,b,c,d), (f,g), (n,p,x)]
sub_index_a = [(0,2),(1),(0,1)]
Output:
output_list = [(a,c), (g), (n,p)]
list_a = [('a', 'b', 'c', 'd'), ('f', 'g'), ('n', 'p', 'x')]
sub_index_a = [(0, 2), (1,), (0, 1)]
def check(i, e):
r = []
for ee in e:
r.append(list_a[i][ee])
return tuple(r)
outputlist = [check(i, e) for i, e in enumerate(sub_index_a)]
print(outputlist)
This evaluates to
[('a', 'c'), ('g',), ('n', 'p')]
well having ("g") just evaluates to "g",the actual tuple of that would look like ("g",) (tuple(["g"])), same as (1) but I think found a half-decent workaround? Hopefully, this is your desired solution.
list_a = [('a','b','c','d'), ('f','g'), ('n','p','x')]
sub_index_a = [(0,2),(1),(0,1)]
print([tuple([list_a[x][indx] for indx in i]) if type(i) in [tuple, list] else list_a[x][i] for x,i in enumerate(sub_index_a)])
[('a', 'c'), 'g', ('n', 'p')]
if you want everything returned as a tuple you can modify the comprehension to:
print([tuple([list_a[x][indx] for indx in i]) if type(i) in [tuple, list] else tuple([list_a[x][i]]) for x,i in enumerate(sub_index_a)])
[('a', 'c'), ('g',), ('n', 'p')]
note:
if you want everything to be nested (with a single element) you would want a list of lists; E.g. [[0,2],[1],[0,1]]
Use zip and a nested comprehension:
list_a = [("a","b","c","d"), ("f","g"), ("n","p","x")]
sub_index_a = [(0,2),(1,),(0,1)] # note the comma in the second tuple
output_list = [tuple(sub[i] for i in i_s) for sub, i_s in zip(list_a, sub_index_a)]
# [('a', 'c'), ('g',), ('n', 'p')]
Thoughts on how I would do this? I want the first value in the tuple to pair with each successive value. This way each resulting tuple would be a pair starting with the first value.
I need to do this: [(a,b,c)] --> [(a,b),(a,c)]
You can try this.
(t,)=[('a','b','c')]
[(t[0],i) for i in t[1:]]
# [('a', 'b'), ('a', 'c')]
Using itertools.product
it=iter(('a','b','c'))
list(itertools.product(next(it),it))
# [('a', 'b'), ('a', 'c')]
Using itertools.repeat
it=iter(('a','b','c'))
list(zip(itertools.repeat(next(it)),it))
# [('a', 'b'), ('a', 'c')]
a = [('a','b','c')]
a = a[0]
a = [tuple([a[0], a[index]]) for index in range(1, len(a))]
Try this !
A solution that uses itertools's combinations module.
from itertools import combinations
arr = (['a','b','c'])
for i in list(combinations(arr, 2)):
if(i[0]==arr[0]):
print(i ,end = " ")
This would give a solution ('a', 'b') ('a', 'c')
You can just append pairs of tuples to a list:
original = [(1,2,3)]
def makePairs(lis):
ret = []
for t in lis:
ret.append((t[0],t[1]))
ret.append((t[0],t[2]))
return ret
print(makePairs(original))
Output:
[(1, 2), (1, 3)]
If your tuples are arbitrary length you can write a simple generator:
def make_pairs(iterable):
iterator = iter(iterable)
first = next(iterator)
for item in iterator:
yield first, item
example result:
my_tuple = ('a', 'b', 'c', 'd')
list(make_pairs(my_tuple))
Out[170]: [('a', 'b'), ('a', 'c'), ('a', 'd')]
This is a memory-efficient solution.
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.
map={"a":5, "b":2, "c":7, "d":5, "e":5}
output should be:
['c', 'a', 'd', 'e', 'b']
So, the code should first assort the dictionary in descending order by its value, and then if its value is the same it should sort by the key in ascending order. So far I have...
newmap=map
newmap=sorted(newmap.iteritems(), key=operator.itemgetter(1,0),reverse=True)
print newmap
This gives me the output [('c', 7), ('e', 5), ('d', 5), ('a', 5), ('b', 2)]. So, I need to get the e, d, a in ascending order... without messing up the sorts of the numbers. How do I do this?
In my answer, I replaced map with dct to not mask the built-in function.
Sorted keys by inverse value, then by key in ascending order:
sorted(dct, key=lambda k: (-dct[k], k))
By turning the value into a negative number, this sorts on value in reverse, while keys are sorted in ascending order.
Demo:
>>> dct = {'a': 5, 'c': 7, 'b': 2, 'e': 5, 'd': 5}
>>> sorted(dct, key=lambda k: (-dct[k], k))
['c', 'a', 'd', 'e', 'b']
Timing comparisons:
>>> import timeit
>>> timeit.timeit("sorted(dct, key=lambda k: (-dct[k], k))", 'from __main__ import dct')
4.741436004638672
>>> timeit.timeit("map(operator.itemgetter(0), sorted(dct.items(), key=lambda i: (-i[1], i[0])))", 'from __main__ import dct; import operator')
7.489126920700073
>>> timeit.timeit("map(operator.itemgetter(0), sorted(sorted(dct.iteritems()), key=operator.itemgetter(1), reverse=True))", 'from __main__ import dct; import operator')
10.01669192314148
Sorting is guaranteed to be stable in Python, so all you have to do is sort twice: first on the key, then on the value.
sorted_pairs = sorted(sorted(map.iteritems()), key=operator.itemgetter(1), reverse=True)
To get just the keys from this output you can use a list comprehension:
[k for k,v in sorted_pairs]
P.S. don't name your variables the same as Python types or you're going to be very surprised some day.
Lets say you have a list like:
l = [('a', 1), ('c', 1), ('b', 1), ('aa', 2), ('aa', 3), ('aa', 1)]
Case 1: Expected: Values lowest first, then for same value, key wise in ascending order.
Idea:
Combine key value in a way that numbers get first priority , then keys.
>>> sorted(l, key=lambda y: str(y[1])+y[0])
[('a', 1), ('aa', 1), ('b', 1), ('c', 1), ('aa', 2), ('aa', 3)]
Case 2: Expected: Highest value to be shown first. If a key has same value, sort in ascending order of alphabets.
>>> sorted(l, key=lambda y: str(10-y[1]) + y[0])
[('aa', 3), ('aa', 2), ('a', 1), ('aa', 1), ('b', 1), ('c', 1)]
I was wondering how to reduce a list of tuples like the following:
[('a','b'),('b','a'),('c','d')]
to the following:
[('a','b'),('c','d')]
And also count the number of times an element repeats and return a list that associates the count with the tuple. From this example, that list would be [2, 1]
Thanks!
I've tried:
l = [('a','b'),('c','d')]
counts_from_list = [len(list(group)) for group in itertools.groupby(my_list)]
zip(set(l), counts_from_list)
Use a Counter, sorting the items first to make sure ('a', 'b') and ('b', 'a') are the "same".
>>> data = [('a','b'),('b','a'),('c','d')]
>>> data = [tuple(sorted(x)) for x in data]
>>> from collections import Counter
>>> c = Counter(data)
>>> c
Counter({('a', 'b'): 2, ('c', 'd'): 1})
Accessing the data
>>> c.keys()
[('a', 'b'), ('c', 'd')]
>>> c.values()
[2, 1]
>>> c.items()
[(('a', 'b'), 2), (('c', 'd'), 1)]
>>>
Use set for to check tuples regardless to order of their items.
my_list = [('a','b'),('b','a'),('c','d')]
my_list = map(set, my_list)
and Instead of
counts_from_list = [len(list(group)) for group in itertools.groupby(my_list)]
You should use
counts_from_list = [len(list(value)) for group, value in itertools.groupby(my_list)]
because itertools.groupby(my_list) returns key, value pairs
but I would advise You to use collections.Counter
collections.Counter(map(frozenset, [('a','b'),('b','a'),('c','d')])).values()
set is not appropriate here because it is not hashable.