Slice array of tuples by its elements - python

For example, I have an array of tuples.
l=[(456,33,1),
(556,22,1),
(123,33,2),
(557,32,2),
(435,21,2)]
I want make the arrays of lists by the last element in each tuple, so that
l=[[(456,33),
(556,22)],
[(123,33),
(557,32),
(435,21)]]
How could I do that? Is there any elegant way?

l = [(456, 33, 1), (556, 22, 1), (123, 33, 2), (557, 32, 2), (435, 21, 2)]
out = {}
for v in l:
out.setdefault(v[-1], []).append(v[:-1])
out = list(out.values())
print(out)
Prints:
[[(456, 33), (556, 22)],
[(123, 33), (557, 32), (435, 21)]]

Another way of doing this would be with itertools.groupby.
from itertools import groupby
from operator import itemgetter
res = [[x[:-1] for x in g] for _, g in groupby(sorted(l, key=itemgetter(2)), itemgetter(2))]
which produces the desired:
[[(456, 33), (556, 22)], [(123, 33), (557, 32), (435, 21)]]
Note that if you can guarantee that the original list l is sorted by the 3rd item in the tuples, you can replace sorted(l, key=itemgetter(2)) with just l.

An alternative attempt is using this:
l=[(456,33,1),
(556,22,1),
(123,33,2),
(557,32,2),
(435,21,2)]
dict_ = {}
for v in l:
val1, val2, id_ = v
dict_[id_] = [[val1, val2]] if id_ not in dict_ else dict_[id_] + [[val1, val2]]
l = [dict_[x] for x in dict_]

Related

Sort dictionary by value and ignore some values

So there are two tasks that are to be performed simultaneously on a dictionary.
Sort the dictionary and extract the top 10 keys along with their values
Ignore keys which have a certain value while reporting top 10
For e.g.
my_dict = { 'abc': 10, 'def':20, 'ghi':100, 'jkl':30, 'mno':101}
So, if I want top 3 while ignoring keys with value 100 and 101 is
result_top3 = [ ('jkl',30), ('def',20), ('abc',10)]
Right now I am using the following :
my_result = dict(Counter(my_dict).most_common()[:3])
but it has two problems:
I don't know how to add a filter in my expression to ignore certain values
It returns a dictionary, in which the keys may be unsorted although they would be top 10 values.
I was hoping if there was a way to do it in one go, or a more Pythonic and efficient way instead of doing it in 2 steps, like removing the keys I don't want and then sorting.
EDIT : It is not necessary to sort the complete dictionary. I just need to extract the top 10 results along with their values.
to filter data
[items for items in my_dict.items() if items[1] < 100]
to sort dict by key value
sorted(my_dict.items(), key=lambda x: -x[1])
and full your solution
sorted([items for items in my_dict.items() if items[1] < 100], key=lambda x: -x[1])[:3]
Do this-
>>> my_dict = { 'abc': 10, 'def':20, 'ghi':100, 'jkl':30, 'mno':101}
>>> dict_tuple = my_dict.items()
>>> print dict_tuple
[('jkl', 30), ('abc', 10), ('ghi', 100), ('def', 20), ('mno', 101)]
>>> dict_tuple.sort(key=lambda x: x[1])
>>> print dict_tuple
[('abc', 10), ('def', 20), ('jkl', 30), ('ghi', 100), ('mno', 101)]
>>> print dict(dict_tuple[:3])
{'jkl': 30, 'abc': 10, 'def': 20}
Almost the same as #BearBrown's answer. But using built-in features and break it down, for doing it step-by-step:
Python 3.6.3 (v3.6.3:2c5fed86e0, Oct 3 2017, 00:32:08)
Type 'copyright', 'credits' or 'license' for more information
IPython 6.0.0 -- An enhanced Interactive Python. Type '?' for help.
In [1]: from collections import OrderedDict
In [2]: my_dict = { 'abc': 10, 'def':20, 'ghi':100, 'jkl':30, 'mno':101}
# filter values out, can be `if v >= 100`(this is depends on condition)
In [3]: filtered_values = ((k, v) for k, v in my_dict.items() if v not in [100, 101])
In [4]: filtered_values
Out[4]: <generator object <genexpr> at 0x1060a9ca8>
In [5]: import operator
# sort by values(second element of key-value tuple) in reverse order
In [6]: top_three = sorted(filtered_values, key=operator.itemgetter(1), reverse=True)[:3]
In [7]: top_three
Out[7]: [('jkl', 30), ('def', 20), ('abc', 10)]
In [8]: OrderedDict(top_three)
Out[8]: OrderedDict([('jkl', 30), ('def', 20), ('abc', 10)])
In the end you will get OrderedDict(dict that have order) as you wanted.
You can do it stepwise:
my_dict = { 'abc': 10, 'def':20, 'ghi':100, 'jkl':30, 'mno':101}
filterIt = [(x[1],x[0]) for x in my_dict.items() if x[1] not in [101,100]]
sortOfSorted = sorted(filterIt, reverse = True)
print (my_dict)
print(filterIt)
print (sortOfSorted)
The result is a list of tuples with the "value" on [0] - no lambda needed for sorting that way as tuples get sorted by [0] first, then [1]
Dictionarys like sets are inherently unordered. And to get the top 10 out of anything you need to sort all of the items to find the biggest ones.
Output:
{'jkl': 30, 'abc': 10, 'ghi': 100, 'def': 20, 'mno': 101}
[(30, 'jkl'), (10, 'abc'), (20, 'def')]
[(30, 'jkl'), (20, 'def'), (10, 'abc')]
you can try:
operator.itemgetter(1) will sort dict by value; and operator.itemgetter(0) will sort dict by key
>>> import operator
>>> my_dict = { 'abc': 10, 'def':20, 'ghi':100, 'jkl':30, 'mno':101}
>>> sorted_my_dict = sorted(my_dict.items(), key=operator.itemgetter(1))
>>> sorted_my_dict
[('abc', 10), ('def', 20), ('jkl', 30), ('ghi', 100), ('mno', 101)]
>>> sorted_my_dict[:3]
[('abc', 10), ('def', 20), ('jkl', 30)]

How to change list values in dictionary in python?

I have a dictionary like this,
{'A':[[1,30],[40,50],[60,79]]
'B':[[2,23],[26,43],[59,119]]
'C':[[1,100],[149,167],[199,250]]
... }
I had the MAX value which is 600. Is it possible to calculate the difference between each value in every list?
Finally, I can get a dictionary like this
{'A':[[31,39],[51,59],[80,600]]
'B':[[24,25],[44,58],[120,600]]
'C':[[101,148],[168,198],[251,600]]
...
}
Let me explain how final output comes.
The origin data is
'A':[[i1,i2],[i3,i4],[i5,i6]]
Max value is M,
For each list, final output will be like
'A':[[(i2)+1,(i3)-1],[(i4)+1,(i5)-1],[(i6)+1,M]]
It seems I need a for loop to get there. But I have no idea how to do this in dictionary.
You can try this:
d1 = {'A':[[1,30],[40,50],[60,79]],
'B':[[2,23],[26,43],[59,119]],
'C':[[1,100],[149,167],[199,250]]}
def split_l(iterable, n):
return [iterable[i:i+n] for i in range(0, len(iterable), n)]
d2 = {k:split_l([val + (-1)**idx for idx, val in enumerate(sum(v, [])[1:])] + [600], 2) for k,v in d1.items()}
Or in more readable way:
d2 = {}
for k, v in d1.items():
flat_v = sum(v, [])[1:]
for idx, __ in enumerate(flat_v):
flat_v[idx] += (-1)**idx
flat_v.append(600)
split_v = [flat_v[i:i+2] for i in range(0, len(flat_v), 2)]
d2[k] = split_v
Results:
import pprint
pprint.pprint(d2)
{'A': [[31, 39], [51, 59], [80, 600]],
'B': [[24, 25], [44, 58], [120, 600]],
'C': [[101, 148], [168, 198], [251, 600]]}
YOu can you itertools.chain
>>> from itertools import chain
>>> d = {'A':[[1,30],[40,50],[60,79]], 'B':[[2,23],[26,43],[59,119]],}
>>> for key in d:
... d[key] = zip(*[iter([each+((-1)**index) for index,each in enumerate(chain.from_iterable(d[key]))]+[600,])]*2)
...
>>> d
{'A': [(31, 39), (51, 59), (80, 600)], 'B': [(24, 25), (44, 58), (120, 600)]}
That is,
>>> chain.from_iterable(d['A'])
<itertools.chain object at 0x7f6a9dd69950>
>>> list(chain.from_iterable(d['A']))
[31, 39, 51, 59, 80, 600]
>>> iter(each for each in enumerate(chain.from_iterable(d[key])))
<generator object <genexpr> at 0x7f6a9dd804b0>
>>> zip(*[iter(each for each in enumerate(chain.from_iterable(d[key])))]*2)
[((0, 2), (1, 23)), ((2, 26), (3, 43)), ((4, 59), (5, 119))]
keep in mind that in python you can swap values of two variables this way:
a, b = b, a
so your problem can be solved like this:
def my_func(a_dict, max):
for row in a_dict.values():
row[0][0], row[0][1], row[1][0], row[1][1], row[2][0], row[2][1] = \
row[0][1]+1, row[1][0]-1, row[1][1]+1, row[2][0]-1, row[2][1]+1, max
One thing to notice is that there is no return .... dictionary is mutable, so any change to a_dict will retain.
After calling my_func, the dictionary passed to the function will contain the result.
update
#MaximTitarenko told me that the number of inner lists in each row(row=a list associated with a key of the dictionary)may vary.
so I wrote another script which works in that case:
def make_result(a_dict, max):
for row in a_dict.values():
for index in range(len(row)-1):
row[index][0] = row[index][1] + 1
row[index][1] = row[index+1][0] -1
row[-1][0] = row[-1][1] + 1
row[-1][1] = max

Comparing a list of tuples

I want to compare a list of tuples, if the first elements are the same then compare the second elements and return the highest.
lis = [(1,10, "n"), (1,15,"n1"), (2,20,"n"),(2,35,"n1"),(3,123,"n"),(3,12,"n1")]
return:
lis = [(1,15,"n1"), (2,35,"n1"), (3,123,"n")]
I'm not sure how to go about this, any help would be appreciated.
I'd use itertools.groupby to first group all items with the same first element together, then max to find the items with the max second element.
Unlike some other answers you may have varying number of elements from different "groups".
from itertools import groupby
from operator import itemgetter
lis = [(1,10, "n"), (1,15,"n1"), (2,20,"n"), (2,35,"n1"), (3,123,"n"),(3,12,"n1")]
lis.sort(key=itemgetter(0)) # groupby requires the iterable to be sorted,
# so making sure it is
grouped_by = groupby(lis, key=itemgetter(0))
output = [max(li, key=itemgetter(1)) for group, li in grouped_by]
print(output)
# [(1, 15, 'n1'), (2, 35, 'n1'), (3, 123, 'n')]
Tuple comparisons do that already, comparing first elements then second and continuing until a tie-breaker is found.
All you need to do is zip the list in such a way to create the correct comparisons:
zip(lis[::2], lis[1::2])
# This produces:
# (1, 10, 'n') (1, 15, 'n1')
# (2, 20, 'n') (2, 35, 'n1')
# (3, 123, 'n') (3, 12, 'n1')
Creates the pairs you need, you can then compare them inside a list-comprehension to get the wanted results:
r = [i if i > j else j for i,j in zip(lis[::2], lis[1::2])]
print(r)
# [(1, 15, 'n1'), (2, 35, 'n1'), (3, 123, 'n')]
The solution using range() and max() functions:
lis = [(1,10, "n"), (1,15,"n1"), (2,20,"n"),(2,35,"n1"),(3,123,"n"),(3,12,"n1")]
result = [max(lis[i:i+2]) for i in range(0, len(lis), 2)]
print(result)
The output:
[(1, 15, 'n1'), (2, 35, 'n1'), (3, 123, 'n')]

How to remove character from tuples in list?

How to remove "(" ,")" form
[('(10', '40)'), ('(40', '30)'), ('(20', '20)')]
by python?
Straightforward, use list comprehension and literal_eval.
>>> from ast import literal_eval
>>> tuple_list = [('(10', '40)'), ('(40', '30)'), ('(20', '20)')]
>>> [literal_eval(','.join(i)) for i in tuple_list]
[(10, 40), (40, 30), (20, 20)]
Depending on how you are currently storing the list:
def to_int(s):
s = ''.join(ch for ch in s if ch.isdigit())
return int(s)
lst = [('(10', '40)'), ('(40', '30)'), ('(20', '20)')]
lst = [(to_int(a), to_int(b)) for a,b in lst] # => [(10, 40), (40, 30), (20, 20)]
or
import ast
s = "[('(10', '40)'), ('(40', '30)'), ('(20', '20)')]"
s = s.replace("'(", "'").replace(")'", "'")
lst = ast.literal_eval(s) # => [('10', '40'), ('40', '30'), ('20', '20')]
lst = [(int(a), int(b)) for a,b in lst] # => [(10, 40), (40, 30), (20, 20)]
>>> L = [('(10', '40)'), ('(40', '30)'), ('(20', '20)')]
>>> [tuple((subl[0].lstrip("("), subl[1].rstrip(")"))) for subl in L]
[('10', '40'), ('40', '30'), ('20', '20')]
Or if you wan the numbers in your tuples to eventually be ints:
>>> [tuple((int(subl[0].lstrip("(")), int(subl[1].rstrip(")")))) for subl in L]
[(10, 40), (40, 30), (20, 20)]
You can call .strip('()') on individual items (if they are strings, like in your example) to strip trailing ( and ).
There are multiple ways to apply that on single elements:
list comprehension (most pythonic)
a = [tuple(x.strip('()') for x in y) for y in a]
map and lambda (interesting to see)
Python 3:
def cleanup(a: "list<tuple<str>>") -> "list<tuple<int>>":
return list(map(lambda y: tuple(map(lambda x: x.strip('()'), y)), a))
a = cleanup(a)
Python 2:
def cleanup(a):
return map(lambda y: tuple(map(lambda x: x.strip('()'), y)), a)
a = cleanup(a)
Process the original string instead. Let's call it a.
On a='((10 40), (40 30), (20 20), (30 10))' , you can call
[tuple(x[1:-1].split(' ')) for x in a[1:-1].split(', ')]
The [1:-1] trims brackets from the string, the splits split strings into lists of strings.
The for is a comprehension.
s = "((10 40), (40 30), (20 20), (30 10))"
print [[int(x) for x in inner.strip(' ()').split()] for inner in s.split(',')]
# or if you actually need tuples:
tuple([tuple([int(x) for x in inner.strip(' ()').split()]) for inner in s.split(',')])

removing something from a list of tuples

Say I have a list:
[(12,34,1),(123,34,1),(21,23,1)]
I want to remove the 1 from each tuple in the list so it becomes
[(12,34),(123,34),(21,23)]
You want to truncate your tuples, use a list comprehension:
[t[:-1] for t in listoftuples]
or, as a simple demonstration:
>>> listoftuples = [(12,34,1),(123,34,1),(21,23,1)]
>>> [t[:-1] for t in listoftuples]
[(12, 34), (123, 34), (21, 23)]
Tuples are immutable, so you can not remove an item. However, you can create a new tuple from the old tuple not including the elements you do not want to. So, to delete an arbitrary item from each tuple from a list of tuples, you can do:
def deleteItem(lst, toDel):
return [tuple(x for x in y if x != toDel) for y in lst]
Result:
>>> lst = [(12,34,1),(123,34,1),(21,23,1)]
>>> deleteItem(lst, 1)
[(12, 34), (123, 34), (21, 23)]
>>> a=[(12, 34, 1), (123, 34, 1), (21, 23, 1)]
>>> [filter (lambda a: a != 1, x) for x in a]
[(12, 34), (123, 34), (21, 23)]
THis will remove all 1 from the tuple irrespective of index
Since you can't change tuples (as they are immutable), I suggest using a list of lists:
my_list = [[12,34,1],[123,34,1],[21,23,1]]
for i in my_list:
i.remove(1)
return my_list
This returns: [[12, 34], [123, 34], [21, 21]].
python 3.2
1. [(i,v)for i,v,c in list1]
2. list(map(lambda x:x[:2],list1))

Categories