this is my code
def width2colspec(widths):
tupleback = []
a=0
for w in widths:
b=a+w
tupleback.append((a,a+w))
a=b
return tupleback
eg:
widths=[15,9,50,10]
width2colspec(widths)
Result:
[(0, 15), (15, 24), (24, 74), (74,84)]
(first one always has to be a zero)
It works and all(maybe not very elegant tho)
To practice I tried to convert it into a list comprehension one liner but i couldn't make it work, closest i got was this.
widths=[15,9,50,10]
colspecs=list((widths[i],widths[i]+widths[i+1]) for i in range(len(widths)-1))
result:
[(0, 15), (15, 24), (9, 59), (50,60)]
(its not maintaining the sum trough the loop)
So my question is, is it possible?
You can do this as a pure list comprehension, but it involves a lot of re-computation, so I wouldn't recommend actually doing it this way.
Start by building a list of all the sums (note that this is re-summing the same numbers over and over, so it's less efficient than your original code that keeps a running sum):
>>> [sum(widths[:i]) for i in range(len(widths)+1)]
[0, 15, 24, 74, 84]
and then iterate over that to produce your list of tuples:
>>> [tuple([sum(widths[:i]) for i in range(len(widths)+1)][i:i+2]) for i in range(len(widths))]
[(0, 15), (15, 24), (24, 74), (74, 84)]
Note that we're now re-computing all those sums again because we didn't assign the original list to a variable.
If you're using Python 3.8 or later, you can use an assignment expression (utilizing the walrus operator :=) to do this in a single list comprehension. I'm "cheating" a little by initializing last via a default argument, but this isn't strictly necessary (I just wanted to make it a "one-liner").
def width2colspec(widths, last=0):
return [(last, last := last+width) for width in widths]
I don't find it that confusing, to be honest.
Related
My apologies if this has been asked before, but I'm trying to implement the itertools permutations and combinations tools, but I can't get the packages to give the precise output I'm looking for. For instance, with the input:
[2,3,5,7,11,13,17,19]
I wish to output all possible ways of 'splitting' the list into tuples (not necessarily keeping the order of the given list):
[(2,3)(5)(7)(11)(13)(17)(19), (2,5)(3)(7)(11)(13)(17)(19), ..., (2,3,7)(5,11,17)(13)(19), ..., (2,3,5,7,11)(13,17,19)]
In other words, you can have more than one combination per element in the new list.
However I've got the following code (not claiming authorship to this line, though) to output all the isolated tuples:
[a for l in range(2,5) for a in itertools.combinations([2,3,5,7,11,13,17,19], l)].
i.e.:
[(2, 3), (2, 5), (2, 7), (2, 11), ..., (7, 13, 17, 19), (11, 13, 17, 19)]
Is there a function that can be used to give the desired output? (i.e. all possible groupings within all elements
Thank you!
This question already has answers here:
Remove partially duplicate tuples from list of tuples
(3 answers)
Making a sequence of tuples unique by a specific element
(1 answer)
Closed 3 years ago.
I have a list of tuples like this one here:
test = [('ent1', 24), ('ent2',12), ('ent3',4.5), ('ent1', 4), ('ent2', 3.5)]
I would like to remove those tuples from the list where the first element has already appeared. So the desired output would be
[('ent1', 24), ('ent2',12), ('ent3',4.5)]
I have no idea how to do this. Normally, if I would like to remove exact duplicated tuples, I would use
list(set(test))
but this is not working in this case. Has anybody an appropriate approach for this problem?
How do you like the output of dict(test)?
{'ent1': 4, 'ent2': 3.5, 'ent3': 4.5}
Or you may want to convert this back to a list of tuples with
>>> list(dict(test).items())
[('ent1', 4), ('ent2', 3.5), ('ent3', 4.5)]
Edit: This will keep the last assigned value but you can also keep the first assigned value by reversing first your list:
>>> list(dict(reversed(test)).items())
[('ent2', 12), ('ent1', 24), ('ent3', 4.5)]
Edit2: If you want to preserve list order, as well, this seems to be a good one-liner solution (inspired by Julien's answer):
>>> [(uk,next(v for k,v in test if k == uk)) for uk in dict(test).keys()]
[('ent1', 24), ('ent2', 12), ('ent3', 4.5)]
And finally, you with functools.reduce you can get another one-liner:
>>> from functools import reduce
>>> reduce(lambda lu,i:i[0] in dict(lu).keys() and lu or lu+[i], test, [])
[('ent1', 24), ('ent2', 12), ('ent3', 4.5)]
Explanation: lu is the list with only unique keys, i is the next item from the test list. If i[0], i.e. the key of the next element is in lu already, we keep lu, otherwise we append i.
Using a check flag
Ex:
test = [('ent1', 24), ('ent2',12), ('ent3',4.5), ('ent1', 4), ('ent2', 3.5)]
check_val = set() #Check Flag
res = []
for i in test:
if i[0] not in check_val:
res.append(i)
check_val.add(i[0])
print(res)
Output:
[('ent1', 24), ('ent2', 12), ('ent3', 4.5)]
test = [('ent1', 24), ('ent2',12), ('ent3',4.5), ('ent1', 4), ('ent2', 3.5)]
deduplicated_test = [(s,[t[1] for t in test if t[0] == s][0]) for s in sorted(set([t[0] for t in test]))]
Short and painful to read, sorry.
I don't remember why sorted(set()) works and set() doesn't but anyway...
If I have a list such as [(10, 22), (12, 50), (13, 15)] and would like to append the difference of these numbers so that the list would look like [(12, 10, 22), (38, 12, 50), (2, 13, 15)] how can I do this?
I have this line of code newList = [[???]+list(tup) for tup in list] but am not sure what to put where the question marks are to get what I want.
Thanks a lot
tuples can't be modified (they are immutable). So you will have to create new tuples. It looks like you are prepending the difference rather than appending.
newList = [(b-a, a,b) for (a,b) in oldList]
I'm trying to figure out how to manually enumerate a list however I'm stuck as I cannot figure out how to split up the data list. This is the code I have so far..
enumerated_list = []
data = [5, 10, 15]
for x in (data):
print(x)
for i in range(len(data)):
enumerate_rule = (i, x)
enumerated_list.append(enumerate_rule)
print(enumerated_list)
This prints out..
5
10
15
[(0, 15), (1, 15), (2, 15)]
When what I'm after is [(0, 5), (1, 15), (2, 15)]. How would I go about this?
Use the enumerate() built-in:
>>> list(enumerate([5, 15, 15]))
[(0, 5), (1, 15), (2, 15)]
Your original code's fault lies in the fact you use x in your loop, however, x doesn't change in that loop, it's simply left there from the previous loop where you printed values.
However, this method of doing it is a bad way. Fixing it would require looping by index, something which Python isn't designed to do - it's slow and hard to read. Instead, we loop by value. The enumerate() built-in is there to do this job for us, as it's a reasonably common task.
If you really don't want to use enumerate() (which doesn't ever really make sense, but maybe as an arbitrary restriction trying to teach you about something else, at a stretch), then there are still better ways:
>>> from itertools import count
>>> list(zip(count(), [5, 15, 15]))
[(0, 5), (1, 15), (2, 15)]
Here we use zip(), which is the python function used to loop over two sets of data at once. This returns tuples of the first value from each iterable, then the second from each, etc... This gives us the result we want when combined with itertools.count(), which does what it says on the tin.
If you really feel the need to build a list manually, the more pythonic way of doing something rather unpythonic would be:
enumerated_list = []
count = 0
for item in data:
enumerated_list.append((count, item))
count += 1
Note, however, that generally, one would use a list comprehension to build a list like this - in this case, as soon as one would do that, it makes more sense to use one of the earlier methods. This kind of production of a list is inefficient and hard to read.
Since x goes through every element in `data, at the end of:
for x in (data):
print(x)
x will be the last element. Which is why you get 15 as the second element in each tuple:
[(0, 15), (1, 15), (2, 15)]
You only need one loop:
for i in range(len(data)):
enumerate_rule = (i, data[i]) # data[i] gets the ith element of data
enumerated_list.append(enumerate_rule)
enumerate_rule = (i, x) is the problem. You are using the same value (x, the last item in the list) each time. Change it to enumerate_rule = (i, data[i]).
I would use a normal "for loop" but with enumerated(), so you can use an index i in the loop:
enumerated_list=[]
data = [5, 10, 15]
for i,f in enumerate(data):
enumerated_list.append((i,f))
print enumerated_list
Result:
[(0, 5), (1, 15), (2, 15)]
If I have a list of tuples, where each tuple represents variables, a, b and c, how can I eliminate redundant tuples?
Redundant tuples are those where a and b are simply interchanged, but c is the same. So for this example:
tups = [(30, 40, 50), (40, 30, 50), (20, 48, 52), (48, 20, 52)]
my final list should only contain only half of the entries. One possible output:
tups = [(30, 40, 50), (20, 48, 52)]
another
tups = [(40, 30, 50), (20, 48, 52)]
etc.
Is there an easy Pythonic way to do this?
I tried using sets, but (30, 40, 50) is different from (40, 30, 50), but to me these are redundant and I'd just like to keep one of them (doesn't matter which, but if I could pick I'd prefer the low to high value order). If there was a way to sort the first 2 elements of the tuples, then using the set would work.
I am sure I could hack together a working solution (perhaps converting tuples to lists as intermediate step), but I just wanted to see if there's an easy and obvious way to do this that I'm not familiar with.
PS: This question partially motivated by PE #39. But even aside from this PE problem, I am now just curious how this could be done easily (or if).
Edit:
Just to provide a bit of context for those not familiar with PE #39 - a, b, and c represent sides of a right triangle, so I'm checking if a**2 + b**2 == c**2, clearly the order of a and b don't matter.
set([(a,b,c) if a<b else (b,a,c) for a,b,c in tups])
From your question, it seems that the first two elements of your tuples form a sub-unit within the tuple. Therefore it would seem to make sense to restructure your data as a tuple of a tuple and a third number, where the first tuple is the first two numbers in sorted order. Then you can naturally use sets:
>>> newTups = [(tuple(sorted([a, b])), c) for a, b, c in tups]
>>> newTups
[((30, 40), 50), ((30, 40), 50), ((20, 48), 52), ((20, 48), 52)]
>>> set(newTups)
set([((20, 48), 52), ((30, 40), 50)])
tups = [(30, 40, 50), (40, 30, 50), (20, 48, 52), (48, 20, 52)]
no_duplicates = list(set(tuple(sorted(tup)) for tup in tups))
Of course this is assuming that the 3rd element of each tuple will always be the largest element in each tuple, otherwise, do this:
no_duplicates = list(set(tuple(sorted(tup[:2])) + (tup[2],) for tup in tups))
As WolframH suggested, the expression tuple(sorted(tup[:2])) + (tup[2],) can be written as tuple(sorted(tup[:2])) + tup[2:], which is advantageous because it can be generalized to tuple(sorted(tup[:i])) + tup[i:], where i can be any point that one wants to separate the sorted elements from the unsorted elements.
Convert each of your tuples into a frozenset and create a set of these frozensets.
tups = [(30, 40, 50), (40, 30, 50), (20, 48, 52), (48, 20, 52)]
frozen_sets = { frozenset(x) for x in tups }
tups2 = [tuple(x) for x in frozen_sets]
This works because frozenset([1,2,3]) == frozenset([3,1,2]), in contrast to tuples, where (1,2,3) != (3,1,2).
You have to convert the tuples into frozensets rather than simple sets because you get the following error when you try to make one set a member of another set:
TypeError: unhashable type: 'set'
frozensets are hashable, and so avoid this problem.
If you do not care about the order for the first two elements, you don't really want to use 3-uples : just convert to a new data structure which discards the information you do not need.
result = {({x[0],x[1]},x[2]) for x in tups}