Consider two lists of tuples:
data1 = [([X1], 'a'), ([X2], 'b'), ([X3], 'c')]
data2 = [([Y1], 'a'), ([Y2], 'b'), ([Y3], 'c')]
Where len(data1) == len(data2)
Each tuple contains two elements:
list of some strings (i.e [X1])
A common element for data1 and data2: strings 'a', 'b', and so on.
I would like to combine them into following:
[('a', [X1], [Y1]), ('b', [X2], [Y2]),...]
Does anyone know how I can do this?
You can use zip function and a list comprehension:
[(s1,l1,l2) for (l1,s1),(l2,s2) in zip(data1,data2)]
#Kasramvd's solution is good if the order is the same among all elements in the data lists. If they are not, it doesn't take that into account.
A solution that does, utilizes a defaultdict:
from collections import defaultdict
d = defaultdict(list) # values are initialized to empty list
data1 = [("s1", 'a'), ("s2", 'c'), ("s3", 'b')]
data2 = [("s1", 'c'), ("s2", 'b'), ("s3", 'a')]
for value, common in data1 + data2:
d[common].append(value)
In order to get a list of it, simply wrap it in a list() call:
res = list(d.items())
print(res)
# Prints: [('b', ['s3', 's2']), ('a', ['s1', 's3']), ('c', ['s2', 's1'])]
We can do this in a single comprehension expression, using the reduce function
from functools import reduce
from operator import add
[tuple([x]+reduce(add,([y[0]] for y in data1+data2 if y[1]==x))) for x in set(y[1] for y in data1+data2)]
If the lists are large, so that data1+data2 imposes a severe time or memory penalty, it might be better to pre-compute it
combdata = data1+data2
[tuple([x]+reduce(add,[y[0]] for y in combdata if y[1]==x))) for x in set(y[1] for y in combdata)]
This solution does not rely on all "keys" occurring in both lists, or the order being the same.
If returned order is important, we can even do
sorted([tuple([x]+reduce(add,([y[0]] for y in data1+data2 if y[1]==x))) for x in set(y[1] for y in data1+data2)],key = lambda x,y=[x[0] for x in data1+data2]: y.index(x[1]))
to ensure that the order is the same as in the original lists. Again, pre-computing data1+data2 gives
sorted([tuple([x]+reduce(add,([y[0]] for y in combdata if y[1]==x))) for x in set(y[1] for y in combdata)],key = lambda x,y=[x[0] for x in combdata]: y.index(x[1]))
Related
I have a list of tuples like so:
x = [("a","b","c"), ("d","e","f"), ("g","h","i"), ("j","k","l")]
and I would like to remove the first element from each index like so:
x = [("b","c"), ("e","f"), ("h","i"), ("k","l")]
I've tried using pop.x and remove.x like so which doesn't work. I think because the list has tuples which cannot be changed?:
for i in result:
i.pop(0)
So I tried to convert the list of tuples to list of lists using a zip function but i get an error:
AttributeError: type object 'zip' has no attribute 'result'
Any ideas what i'm doing wrong?
Thanks for all the useful answers! Many ways to skin a cat that i can use for other problems i encounter =D
A tuple in python is an unchangeable object. You can store anything you want in it but once it is declared you cannot change it back.
Here a link to tuples : https://docs.python.org/3/tutorial/datastructures.html#tuples-and-sequences (section 5.3)
The popfunction deletes the last element from your list and returns it. Since you have tuples in your list, calling the pop function on your list of tuples will only result in returning and deleting the last tuple from your list.
Tuples are immutable, so you can't change them. You can however overwrite your list with a list of new tuples:
x = [('a','b','c'), ('d','e','f'), ('g','h','i'), ('j','k','l')]
x = [tpl[1:] for tpl in x]
Output:
[('b', 'c'), ('e', 'f'), ('h', 'i'), ('k', 'l')]
As you've found, tuples are immutable so you must create a new list with tuples that don't contain the items
new_list = []
for i in result:
new_list.append(i[1:])
or replace the list items by their index
for idx, tup in enumerate(x):
x[idx] = tup[1:]
or as a list comprehension
[i[1:] for i in result]
x = [('a','b','c'), ('d','e','f'), ('g','h','i'), ('j','k','l')]
y = []
for t in x:
y.append(tuple(list(t)[1:]))
print(y)
Output
[('b', 'c'), ('e', 'f'), ('h', 'i'), ('k', 'l')]
Given an iterable consisting of a finite set of elements:
(a, b, c, d)
as an example
What would be a Pythonic way to generate the following (pseudo) ordered pair from the above iterable:
ab
ac
ad
bc
bd
cd
A trivial way would be to use for loops, but I'm wondering if there is a pythonic way of generating this list from the iterable above ?
Try using combinations.
import itertools
combinations = itertools.combinations('abcd', n)
will give you an iterator, you can loop over it or convert it into a list with list(combinations)
In order to only include pairs as in your example, you can pass 2 as the argument:
combinations = itertools.combinations('abcd', 2)
>>> print list(combinations)
[('a', 'b'), ('a', 'c'), ('a', 'd'), ('b', 'c'), ('b', 'd'), ('c', 'd')]
You can accomplish this in a list comprehension.
data = [1, 2, 3, 4]
output = [(data[n], next) for n in range(0,len(data)) for next in data[n:]]
print(repr(output))
Pulling the comprehension apart, it's making a tuple of the first member of the iterable and the first object in a list composed of all other members of the iterable. data[n:] tells Python to take all members after the nth.
Here it is in action.
Use list comprehensions - they seem to be considered pythonic.
stuff = ('a','b','c','d')
Obtain the n-digit binary numbers, in string format, where only two of the digits are one and n is the length of the items.
n = len(stuff)
s = '{{:0{}b}}'.format(n)
z = [s.format(x) for x in range(2**n) if s.format(x).count('1') ==2]
Find the indices of the ones for each combination.
y = [[i for i, c in enumerate(combo) if c == '1'] for combo in z]
Use the indices to select the items, then sort.
x = [''.join(operator.itemgetter(*indices)(stuff)) for indices in y]
x.sort()
Hopefully this will make sense...
I have a list of tuples of the following form:
list_of_tuples = [('a', 1), ('b', 3), ('b', 2), ('a', 3)]
The desired output is
sorted_list_of_tuples = [('a', 1), ('b', 2), ('b', 3), ('a', 3)]
The issue is I want the second entry to be increasing, and the first entry to be decreasing.
import operator as op
sorted_list_of_tuples = sorted(list_of_tuples, key=op.itemgetter(2, 0))
This, of course, sorts both fields to be increasing. I can't come up with a cute (i.e., couple of lines) way to do this. Does anyone have a way to accomplish this kind of sorting easily?
I seem to remember that you can reference elements of a list comprehension inside the brackets using _, so maybe that's a place to start?
Maybe I wasn't clear: the integer in this case is more important. It's order should be increasing across the list. When there's a tie (ie.., the second entry is equal), I want 'b' to occur before 'a'.
If you can describe the key in English, just translate that to a function.
I want the second entry to be increasing, and the first entry to be decreasing.
So, the key is:
def make_key(my_tuple):
return my_tuple[1], -my_tuple[0]
Except, of course, that - doesn't work that way on strings, so you need something fancier.
Or, maybe not… while the first element of each tuple is a string, the second is an integer, so, we can just negate the key function, and use reverse to un-negate it:
def make_key(my_tuple):
return -my_tuple[1], my_tuple[0]
sorted_list_of_tuples = sorted(list_of_tuples, key=make_key, reverse=True)
If you want to save a few keystrokes:
sorted_list_of_tuples = sorted(list_of_tuples,
key=lambda x: (x[1], x[0]), reverse=True)
This isn't the only trick that would work here. For example, because all of your strings are 1-character strings, ord(x) < ord(y) iff x < y.
But sometimes you can't think of an easy trick—but you can think of an easy way to write a comparison function. If it's more readable, do it that way:
def compare_my_tuples(lhs, rhs):
if rhs[1] > lhs[0]: return 1
elif rhs[1] < lhs[0]: return -1
elif rhs[0] > lhs[0]: return -1
elif rhs[0] < rhs[0]: return 1
else: return 0
sorted_list_of_tuples = sorted(list_of_tuples,
key=functools.cmp_to_key(compare_my_tuples))
Or, of course, you can split it into two sorts, as in steveha's answer. (Yes, it might take twice as long… but in most apps, that won't make any difference at all.)
Sure. Python's built-in sort is a "stable" sort. So, pick which sort you want to be more important, and do that one second. Do the less-important sort, then sort again by the more-important criteria.
Working code:
import operator as op
list_of_tuples = [('a', 1), ('b', 3), ('b', 2), ('a', 3)]
list_of_tuples.sort(key=op.itemgetter(0), reverse=True)
list_of_tuples.sort(key=op.itemgetter(1))
assert list_of_tuples == [('a', 1), ('b', 2), ('b', 3), ('a', 3)]
I guess you can do the whole thing in one pass if you come up with a clever key function. Maybe this:
def custom_key_fn(tup):
ch, n = tup # unpack tuple
return (n, -ord(ch))
list_of_tuples = [('a', 1), ('b', 3), ('b', 2), ('a', 3)]
list_of_tuples.sort(key=custom_key_fn)
assert list_of_tuples == [('a', 1), ('b', 2), ('b', 3), ('a', 3)]
I'm making a function that takes a variable number of lists as input (i.e., an arbitrary argument list).
I need to compare each element from each list to each element of all other lists, but I couldn't find any way to approach this.
Depending on your goal, you can make use of some of the itertools utilities. For example, you can use itertools.product on *args:
from itertools import product
for comb in product(*args):
if len(set(comb)) < len(comb):
# there are equal values....
But currently it's not very clear from your question what you want to achieve. If I didn't understand you correctly, you can try to state the question in a more specific way.
I think #LevLeitsky's answer is the best way to do a loop over the items from your variable number of lists. However, if purpose the loop is just to find common elements between pairs of items from the lists, I'd do it a bit differently.
Here's an approach that finds the common elements between each pair of lists:
import itertools
def func(*args):
sets = [set(l) for l in args]
for a, b in itertools.combinations(sets, 2):
common = a & b # set intersection
# do stuff with the set of common elements...
I'm not sure what you need to do with the common elements, so I'll leave it there.
The itertools module provides a lot of useful tools just for such tasks. You can adapt the following example to your task by integrating it into your specific comparison logic.
Note that the following assumes a commutative function. That is, about half of the tuples are omitted for reasons of symmetry.
Example:
import itertools
def generate_pairs(*args):
# assuming function is commutative
for i, l in enumerate(args, 1):
for x, y in itertools.product(l, itertools.chain(*args[i:])):
yield (x, y)
# you can use lists instead of strings as well
for x, y in generate_pairs("ab", "cd", "ef"):
print (x, y)
# e.g., apply your comparison logic
print any(x == y for x, y in generate_pairs("ab", "cd", "ef"))
print all(x != y for x, y in generate_pairs("ab", "cd", "ef"))
Output:
$ python test.py
('a', 'c')
('a', 'd')
('a', 'e')
('a', 'f')
('b', 'c')
('b', 'd')
('b', 'e')
('b', 'f')
('c', 'e')
('c', 'f')
('d', 'e')
('d', 'f')
False
True
if you want the arguments as dictionary
def kw(**kwargs):
for key, value in kwargs.items():
print key, value
if you want all the arguments as list:
def arg(*args):
for item in args:
print item
you can use both
def using_both(*args, **kwargs) :
kw(kwargs)
arg(args)
call it like that:
using_both([1,2,3,4,5],a=32,b=55)
This question already has answers here:
How to sort a list of objects based on an attribute of the objects in descending order?
(9 answers)
Closed 28 days ago.
I have the following tuple, which contains tuples:
MY_TUPLE = (
('A','Apple'),
('C','Carrot'),
('B','Banana'),
)
I'd like to sort this tuple based upon the second value contained in inner-tuples (i.e., sort Apple, Carrot, Banana rather than A, B, C).
Any thoughts?
from operator import itemgetter
MY_SORTED_TUPLE = tuple(sorted(MY_TUPLE, key=itemgetter(1)))
or without itemgetter:
MY_SORTED_TUPLE = tuple(sorted(MY_TUPLE, key=lambda item: item[1]))
From Sorting Mini-HOW TO
Often there's a built-in that will
match your needs, such as str.lower().
The operator module contains a number
of functions useful for this purpose.
For example, you can sort tuples based
on their second element using
operator.itemgetter():
>>> import operator
>>> L = [('c', 2), ('d', 1), ('a', 4), ('b', 3)]
>>> map(operator.itemgetter(0), L)
['c', 'd', 'a', 'b']
>>> map(operator.itemgetter(1), L)
[2, 1, 4, 3]
>>> sorted(L, key=operator.itemgetter(1))
[('d', 1), ('c', 2), ('b', 3), ('a', 4)]
Hope this helps.
sorted(my_tuple, key=lambda tup: tup[1])
In other words, when comparing two elements of the tuple you're sorting, sort based on the return value of the function passed as the key parameter.
I achieved the same thing using this code, but your suggestion is great. Thanks!
templist = [ (line[1], line) for line in MY_TUPLE ]
templist.sort()
SORTED_MY_TUPLE = [ line[1] for line in templist ]