V = [[10,20,30,40],[30,40,50,-50,-70]]
V_max_result = max(V)
V_max_result_index = V.index(max(V))
print(V_max_result,V_max_result_index)
Presently, it is giving output like [30, 40, 50, -50, -70] 1
I wanted it to show something like [[40,3],[50,2]] where 40 is maximum in first list and it is located at 3.
V = [[10,20,30,40], [30,40,50,-50,-70]]
print([[max(per_v), per_v.index(max(per_v))] for per_v in V])
Output:
[[40, 3], [50, 2]]
While the answer by #atline works, it has to iterate three times over each sublist, once to get the maximum, a second time to get the maximum again and then a third time to find its position in the list.
Instead it is easier to find the maximum of tuples, where each tuple is like (value, index). This is almost what enumerate returns (index, value), just reversed. We can use zip(values, itertools.count()) for that. It works, because tuples sort by first sorting according to the first entry, then the second.
from itertools import count
V = [[10,20,30,40], [30,40,50,-50,-70]]
print([max(zip(per_v, count())) for per_v in V])
# [(40, 3), (50, 2)]
If you insist on the inner tuples being lists as well:
print([list(max(zip(per_v, count()))) for per_v in V])
# [[40, 3], [50, 2]]
Related
I have an iterable of unique numbers:
lst = [14, 11, 8, 55]
where every value is somewhere among numbers of dict's iterable-values, say lists:
dict_itms.items() = dict_items([(1, [0, 1, 2, 3]), (2, [11, 14, 12]), (3, [30, 8, 42]), (4, [55, 6])])
I have to find each lst element in a dict such a way that, finally, I would have a list of keys pairwise against each element in lst.
This method:
keys_ = []
for a in lst:
for k, v in dict_itms.items():
if a in v:
keys_ += [k]
break
else:
continue
gives:
[2, 2, 3, 4]
Is there more efficient way to find every key pairwise against each number to find?
You can use any in a list comprehension:
print([k for k,v in dict_itms.items() if any(x in lst for x in v)])
Output:
[2, 3, 4]
Update
According to this answer not set(v).isdisjoint(lst) is the fastest:
print([k for k,v in dict_itms.items() if not set(v).isdisjoint(lst)])
It's unclear what you mean by 'efficient'; do you need this to be efficient in a given pass or in aggregate? The reason I ask is that typically the best way to handle this in aggregate is by doing a pre-processing pass that flips your key-value relation:
reverse_lookup = dict()
for k,v in d.items():
for i in v:
keys = reverse_lookup.get(i, []) # Provide an empty list if this item not yet found
keys.append(k)
reverse_lookup[i] = keys
Now that you have your reverse lookup processed, you can use it in a straightforward manner:
result = [reverse_lookup.get(i) for i in lst]
# `result` is actually a list of lists, so as to allow duplicates. You will need to flatten it, or change the reverse lookup to ignore dupes.
The initial processing for the reverse lookup is O(n*m), where n*m is the total length of the original dictionary values summed. However, each lookup for the lst portion is O(1), so if you squint and have enough lookups this is O(p), where p is the length of lst. This will be wildly more efficient than other approaches if you have to do it a lot, and much less efficient if you're only ever passing over a given dictionary once.
A simple and Pythonic implementation:
d = dict([(1, [0, 1, 2, 3]), (2, [11, 14, 12]), (3, [30, 8, 42]), (4, [55, 6])])
xs = [14, 11, 8, 55]
keys = [k for k, v in d.items() if set(v).intersection(xs)]
print(keys)
However, this doesn't duplicate the 2 key, which your example does - not sure if that's behaviour you need?
I found numerous similar questions in other programming languages (ruby, C++, JS, etc) but not for Python. Since Python has e.g. itertools I wonder whether we can do the same more elegantly in Python.
Let's say we have a "complete range", [1,100] and then a subset of ranges within/matching the "complete range":
[10,50]
[90,100]
How can we extract the not covered positions, in this case [1,9], [51,89]?
This is a toy example, in my real dataset I have ranges up to thousands.
Here is a neat solution using itertools.chain: I've assumed the input ranges don't overlap. If they do, they need to be simplified first using a union-of-ranges algorithm.
from itertools import chain
def range_gaps(a, b, ranges):
ranges = sorted(ranges)
flat = chain((a-1,), chain.from_iterable(ranges), (b+1,))
return [[x+1, y-1] for x, y in zip(flat, flat) if x+1 < y]
Taking range_gaps(1, 100, [[10, 50], [90, 100]]) as an example:
First sort the ranges in case they aren't already in order. If they are guaranteed to be in order, this step is not needed.
Then flat is an iterable which will give the sequence 0, 10, 50, 90, 100, 101.
Since flat is lazily evaluated and is consumed by iterating over it, zip(flat, flat) gives a sequence of pairs like (0, 10), (50, 90), (100, 101).
The ranges required are then like (1, 9), (51, 89) and the case of (100, 101) should give an empty range so it is discarded.
Assuming the list contains only integers, and the sub-ranges are in increasing order and not overlapping, You can use below code.
This code will take all sub ranges one by one, and will compare with original complete range and the sub range before it, to find the missing range.
[start,end]=[1,100]
chunks=[[25,31],[7,15],[74,83]]
print([r for r in [[start,chunks[0][0]-1] if start!=chunks[0][0] else []] + [[chunks[i-1][1]+1, chunks[i][0]-1] for i in range(1,len(chunks))]+[[chunks[-1][1]+1,end] if end!=chunks[-1][1] else []] if r])
Input
[1,100]
[[7,15],[25,31],[74,83]]
Output
[[1, 6], [16, 24], [32, 73], [84, 100]]
If increasing order of sub ranges are not guaranteed. you can include below line to sort chunks.
chunks.sort(key=lambda x: x[0])
This is a generic solution:
def gap(N, ranges):
ranges=[(min1, max1), (min2, (max2), ......, (minn, maxn)]
original=set(range(N))
for i in ranges:
original=original-set(range(i[0], i[1]))
return original
I'm just getting into Python, and having some trouble understanding the control flow and iteration logic.
I am trying to create a function which takes a list of tuples, and I want to return a new list with the maximum element per tuple.
I know I'm missing putting the maximum element into a new list, but first I am trying to get that maximum value.
def max_element_per_tuple(tuple_list):
maximum = tuple_list[0]
for item in tuple_list:
if item > maximum:
maximum = item
return maximum
# test it
tuple_list = [(-1,0,1), (10,20,30), (100,50,25), (55,75,65)]
print(max_element_per_tuple(tuple_list))
This returns: (100, 50, 25)
Want returned: (1, 30, 100, 75)
Or if a list (?), then: [1, 30, 100, 75]
Simply, try this one-linear pythonic solution
>>> tuple_list = [(-1,0,1), (10,20,30), (100,50,25), (55,75,65)]
>>> [max(e) for e in tuple_list] # List
[1, 30, 100, 75]
>>> tuple(max(e) for e in tuple_list) # Tuple
(1, 30, 100, 75)
Right now you are just looping through the tuples and returning the "biggest" one - tuple comparison is done element-wise.
What you want is to add another loop level that will find the maximum of each tuple:
def max_element_per_tuple(tuple_list):
res = []
for tup in tuple_list: # loops over the tuples in the list
maximum = tup[0]
for item in tup: # loops over each item of the tuple
if item > maximum:
maximum = item
res.append(maximum)
return res
This gives as expected:
>>> max_element_per_tuple([(-1, 0, 1), (10, 20, 30), (100, 50, 25), (55, 75, 65)])
[1, 30, 100, 75]
Your function max_element_per_tuple is correct (though unnecessary, because standard function max() exists). What you did wrong was calling that function using a list of tuples as the argument. This found the biggest tuple in the list ("biggest" for tuples means "the one with first element biggest"), which happened to be the third one - (100,50,25). What you need to do is either:
result = list(map(max, tuple_list))
or
result = [max(t) for t in tuple_list]
This last one is roughly equivalent to:
result = []
for t in tuple_list:
result.append(max(t))
If you replace max with your max_element_per_tuple the results should be the same.
This should work
def max_element_per_tuple(tuple_list):
maximum = []
for item in tuple_list:
maximum.append(max(item))
return maximum
will give this output : [1, 30, 100, 75]
The issue: max_element_per_tuple(tuple_list) returns the wrong result because it is looking for the max tuple, not the max value in each tuple.
def max_element_per_tuple(tuple_list):
maximum = tuple_list[0] # maximum is = (-1,0,1)
for item in tuple_list:
if item > maximum: # compares the tuples e.g (-1,0,1) == (10,20,30)
maximum = item
return maximum # at the end you have (100,50,25). it's the max tuple
Try any of below options:
tuple_list = [(-1,0,1), (10,20,30), (100,50,25), (55,75,65)]
# Get the max from each tuple using List comprehension
max_items_list = [max(tuple_item) for tuple_item in tuple_list] # in a list
max_items_tuple = tuple(max(tuple_item) for tuple_item in tuple_list) # in a tuple
print(max_items_list)
print(max_items_tuple)
# Get the max from each tuple using For Loop
# Can be used with List only, tuples are immutable
for_max_items_list = list()
for tuple_item in tuple_list:
max_value = max(tuple_item) # get the max of each tuple e.g Max((-1,0,1)) = 1
for_max_items_list.append(max_value) # add the max to list
print(for_max_items_list)
I am trying to write different implementations for a fractional knapsack problem.
For this I have 2 arrays:
Values
Weights
The elements value[n] corresponds to element weights[n]. So we can calculate value_per_unit as:
for I in range(values):
value_per_unit.append(values[I]/weights[I])
value_per_unit.sort()
I now need the 2 arrays (values and weights) to be sorted according to the value_per_unit array
eg:
If
values = [60, 100, 120]
weights = [20, 50, 30]
Then
values_per_unit = [3.0, 2.0, 4.0]
and so values_per_unit_sorted will be [2.0, 3.0, 4.0]
I need the values and weights arrays to become:
values_sorted = [100,60,120]
weights_sorted = [50,20,30]
Is there a way to achieve this using simple lambda functions?
I can still do something like this, but it seems highly inefficient every-time I need to access the elements:
weights[(value_per_unit_sorted.index(max(value_per_unit_sorted)))]
In one line:
values, weights = zip(*sorted(zip(values, weights), key=lambda t: t[0]/t[1]))
To explain: First, zip the lists to pair them.
pairs = zip(values, weights)
# [(60, 20), (100, 50), (120, 30)]
Then, sort by the quotient of value to weight.
sorted_pairs = sorted(pairs, key=lambda t: t[0]/t[1])
# [(100, 50), (60, 20), (120, 30)]
Finally, unzip them back into separate lists.
values, weights = zip(*sorted_pairs)
# (100, 60, 120), (50, 20, 30)
An alternative is to construct tuples explicitly containing the ratio as the first element.
ratios, values, weights = zip(*sorted((v/w, v, w) for v, w in zip(values, weights)))
The former appears to be slightly faster in some quick testing. If you're looking for an optimal algorithm, you're probably going to have to unroll things and the solution will not be as concise.
And to address the comment from #TomWyllie, if you already have the list of ratios, you can use:
ratios, values, weights = zip(*sorted(zip(ratios, values, weights)))
Note that these last two solutions differ from the initial solution in the case where two pairs have an equal ratio. These solutions will sort secondarily by value, while the first solution will keep the items in the same order as the original list.
An elegant way to do this is to make a multi-dimensional list with the values and weights:
for i in range(len(values)):
values_and_weights.append([values[i], weights[i])
# The resulting list is [[60, 20], [100, 50], [120, 30]]
Then, use the sort method with a value divided by weight as the key.
values_and_weights.sort(key=(lambda x: x[0]/x[1]))
For a more explicit (but arguably less pythonic) solution, create a list of indices, sorted by the value at that index in value_per_unit, and reorder values and weights accordingly.
sorted_indices = [index for index, value in
sorted(enumerate(value_per_unit), key=lambda x: x[1])]
values = [values[i] for i in sorted_indices]
weights = [weights[i] for i in sorted_indices]
print(values, weights)
Outputs:
([100, 60, 120], [50, 20, 30])
You can tidy this up, eliminating the unnecessary extra loops using zip, and with a generator expression;
values, weights = zip(*((values[i], weights[i]) for i, value in
sorted(enumerate(value_per_unit), key=lambda x: x[1])))
print(values)
print(weights)
Which outputs;
(100, 60, 120)
(50, 20, 30)
Note these final values are tuples not lists. If you really need the output to be a list, a simple values, weights = map(list, (values, weights)) should suffice. You could even wrap that into the one liner, although by that point it's probably getting pretty hard to follow what's happening.
The problem you are having is cause by using a calculated field over each element (element I will have the calculated value values[I]/weights[I]).
To solve this and still keep it extremely easy to understand, you can turn it into a tuple of this form: ( calculated_value, (value, weight) ), per element.
This approach keeps it easy to read and understand. Look at the following solution:
values = [60, 100, 120]
weights = [20, 50, 30]
value_per_unit = []
for I in range(len(values)):
value_per_unit.append( (values[I]/weights[I], (values[I], weights[I])) )
sorted_value_per_unit = sorted(value_per_unit, key=lambda x: x[0])
sorted_values = []
sorted_weights = []
for I in range(len(values)):
(value, weight) = sorted_value_per_unit[I][1]
sorted_values.append(value)
sorted_weights.append(weight)
print(str(sorted_values))
print(str(sorted_weights))
Also, note that I modified the loop from your original code:
range(values) was change to range(len(values))
Since range would need the length of the list, rather than the list itself.
GRIDCOLS = 3;
GRIDROWS = 4 ;
gidder = 20;
gridxy = [];
for column in range (GRIDCOLS):
.. yrow = column * 100+ 50
.. for row in range (GRIDROWS):
.. xcell = row * 100+50
.. ycell = yrow + random.randint(-gidder, gidder)
.. gridxy.append([xcell, ycell])
.. print (gridxy)
this would create something like this [50, 58], [150, 56], [250, 39], [350, 52]
sumlist = ['a','b','c','$','4','2','3']
and what i want to do is assigning each of the coordinates [a,b] with each of the elements within the sumlist randomly.
I'm not really sure how to approach this. Do i do something with the index? Please help.
Thank you.
What kind of assignment do you need?
You can use zip([iterable, ...])
This function returns a list of tuples, where the i-th tuple contains the i-th element from each of the argument sequences or iterables.
>>> zip(sumlist, gridxy)
[('a', [50, 58]), ('b', [150, 56]), ('c', [250, 39]), ('$', [350, 52])]
You can create a dict:
>>> dict(zip(sumlist, gridxy))
{'a': [50, 58], 'b': [150, 56], 'c': [250, 39], '$': [350, 52]}
If you need a ordered dictionary you can use collections.OrderedDict
Return an instance of a dict subclass, supporting the usual dict methods.
You must random.shuffle your data...
import random
sumlist = ['a','b','c','$','4','2','3']
random.shuffle(sumlist) # shuffles in place!
At this point you have a random permutation of your data, stored into sumlist and you can proceed with pairing the sumlist and the gridxy elements, usually using the zip builtin (that truncates the pairing at the shortest iterable's length) or zip_longest in the itertools module that allow you to use a default value as a fill-in.
# create a list of tuples
lot = [t for t in zip(sumlist,gridxy)]
# create a dict
d = {s:xy for s, xy in zip(sumlist,gridxy)}
Of course you can use zip_longest if you feel appropriate to use default values in your code.
Pay attention to the fact that random.shuffle operates in place (and returns None), so that if you want to access your data in the original order, then you want to save it somewhere before shuffling
...
original_list = sumlist[:]
random.shuffle(sumlist)
...