Max Value within a List of Lists of Tuple - python

I have a problem to get the highest Value in a dynamic List of Lists of Tuples.
The List can looks like this:
adymlist = [[('name1',1)],[('name2',2),('name3',1), ...('name10', 20)], ...,[('name m',int),..]]
Now I loop through the List to get the highest Value (integer):
total = {}
y=0
while y < len(adymlist):
if len(adymlist) == 1:
#has the List only 1 Element -> save it in total
total[adymlist[y][0][0]] = adymlist[y][0][1]
y += 1
else:
# here is the problem
# iterate through each lists to get the highest Value
# and you dont know how long this list can be
# safe the highest Value in total f.e. total = {'name1':1,'name10':20, ..}
I tried a lot to get the maximum Value but I found no conclusion to my problem. I know i must loop through each Tuple in the List and compare it with the next one but it dont know how to code it correct.
Also I can use the function max() but it doesnt work with strings and integers. f.e.
a = [ ('a',5),('z',1)] -> result is max(a) ---> ('z',1) obv 5 > 1 but z > a so I tried to expand the max function with max(a, key=int) but I get an Type Error.
Hope you can understand what I want ;-)
UPDATE
Thanks so far.
If I use itertools.chain(*adymlist) and max(flatlist, key=lambda x: x[1])
I will get an exception like : max_word = max(flatlist, key=lambda x: x[1])
TypeError: 'int' object is unsubscriptable
BUT If I use itertools.chain(adymlist) it works fine. But I dont know how to summate all integers from each Tuple of the List. I need your help to figure it out.
Otherwise I wrote a workaround for itertools.chain(*adymlist) to get the sum of all integers and the highest integer in that list.
chain = itertools.chain(*adymlist)
flatlist = list(chain)
# flatlist = string, integer, string, integer, ...
max_count = max(flatlist[1:len(flatlist):2])
total_count = sum(flatlist[1:len(flatlist):2])
# index of highest integer
idx = flatlist.index(next((n for n in flatlist if n == max_count)))
max_keyword = flatlist[idx-1]
It still does what I want, but isn't it to dirty?

To clarify, looks like you've got a list of lists of tuples. It doesn't look like we care about what list they are in, so we can simplify this to two steps
Flatten the list of lists to a list of tuples
Find the max value
The first part can be accomplished via itertools.chain (see e.g., Flattening a shallow list in Python)
The second can be solved through max, you have the right idea, but you should be passing in a function rather than the type you want. This function needs to return the value you've keyed on, in this case ,the second part of the tuple
max(flatlist, key=lambda x: x[1])
Correction
I re-read your question - are you looking for the max value in each sub-list? If this is the case, then only the second part is applicable. Simply iterate over your list for each list
A bit more pythonic than what you currently have would like
output = []
for lst in lists:
output.append( max(flatlist, key=lambda x: x[1]) )
or
map(lambda x: max(x, key=lambda y: y[1]) , lists)

As spintheblack says, you have a list of lists of tuples. I presume you are looking for the highest integer value of all tuples.
You can iterate over the outer list, then over the list of tuples tuples like this:
max_so_far = 0
for list in adymlist:
for t in list:
if t[1] > max_so_far:
max_so_far = t[1]
print max_so_far
This is a little bit more verbose but might be easier to understand.

Related

Get the n top elements from a list of lists

I'm using the following example for demonstration purposes.
[["apple",10],
["oranges",5],
["strawberies",2],
["pineapples",12],
["bananas",9],
["tomattoes",8],
["watermelon",1],
["mangos",7],
["grapes",11],
["potattoes",3]]
I want to get say the top 3 fruits by quantity (top 3 elements returned), however i don't want the order to change.
So the end result will be
[["apple",10],
["pineapples",12],
["grapes",11]]
Any help will be appreciated.
arr = [["apple",10],
["oranges",5],
["strawberies",2],
["pineapples",12],
["bananas",9],
["tomattoes",8],
["watermelon",1],
["mangos",7],
["grapes",11],
["potattoes",3]]
sorted_arr = sorted(arr,key=lambda x: x[1],reverse=True)[:3]
output = [elem for elem in arr if elem in sorted_arr]
print(sorted_arr)
print(output)
First, we sort the array in reverse order to get the first 3 elements. Then, we use list comprehension to loop through the original array and check if the elements are in the top 3. This preserves the order.
As you have probably heard before there is a sort method that you can apply to lists. You can pass in the key as a function that will indicate how you want your list to be sorted.
l = [["apple",10],
["oranges",5],
["strawberies",2],
["pineapples",12],
["bananas",9],
["tomattoes",8],
["watermelon",1],
["mangos",7],
["grapes",11],
["potattoes",3]]
l.sort(key = lambda x: x[1]) #to sort by second element
result = l[-3:] # To get the top three elements

Can Python sorting with a key pass me the ordinal position of the item in the original list, instead of the item itself

I have a list of items I need to sort but the the value I want used in the sort for each item is not in the list itself. The sorting information is in another list, which positionally aligns with the first one.
Ie., l = list of items to sort, v = list of values to use for sorting. When sorting l[0], the value in v[0] should be used.
So during the sort, I need python to tell me the ordinal position of the item being sorted, instead of giving the item itself.
So effectively what I would do is this:
l = sort(key = lambda index_of_item: v[index_of_item])
By default I think that this would not work as python is invoking that lambda with an actual item from l, not the item's position. Is there a way to have python give me the position instead?
(If there were some identifier in each item being sorted I could use that myself inside the lambda to extrapolate the index_of_item, but sadly there isn't)
Convert your list of items to a list of tuples that includes the original index; this can be done using enumerate(). Then you can use that index to access the other list.
augumented_list = list(enumerate(l))
augmented_list.sort(key = lambda item: v[item[0]])
result = [x[1] for x in augmented_list]
Another option is to use zip() to combine the elements of both lists, then use the value from the other list as the sort key.
result = [x[0] for x in sorted(zip(l, v), key = lambda x: x[1])]

Using Filter Function to categorize list by specific index + Printing by specific index or list name

I am creating and manipulating a list in Python, I am having trouble categorizing my lists via filter function...
I have 3 lists, that I append into one list, I frequently print (the_list) along the way, here is my code:
list1 = ['Georgia', 10.5, 'Peach'];
list2 = ['Florida', 21.3, 'Sunshine'];
list3= ['Alabama', 4.9, 'Dixie'];
#List that includes list1, list2 and list3 called "the_list"
the_list = []
the_list.append(list1)
the_list.append(list2)
the_list.append(list3)
the_list
#insert new values into the list (these values represent state numbers)
list1.insert(3, 4)
list2.insert(3, 27)
list3.insert(3, 22)
#print the modified list
print (the_list)
#Organize the list from lowest to highest (based off numbers in index 1)
the_list.sort(key=lambda tup: tup[1])
print (the_list)
#filter states by category based off their population
#This is where I need help
#Small States
def lessThanTen(index):
return index < 10
the_list
filter(lessThanTen, the_list)
print (the_list)
#Big States
def greaterThanTen(index):
return index > 10
the_list
filter(greaterThanTen, the_list)
print (the_list)
Is there a way to filter these lists into categories by a specific index number, in this case index [1] (Which is population), and subsequently output these list items by printing, either their list name or their value at index [0]...example 'Georgia' or "list1"
Python filter documentation:
filter(function, iterable) Construct an iterator from those elements
of iterable for which function returns true. iterable may be either a
sequence, a container which supports iteration, or an iterator. If
function is None, the identity function is assumed, that is, all
elements of iterable that are false are removed.
Note that filter(function, iterable) is equivalent to the generator
expression (item for item in iterable if function(item)) if function
is not None and (item for item in iterable if item) if function is
None.
It's unclear what you mean, but I'll try to help you the best I can.
First of all: you're greaterThanTen function takes index as input, at least it looks that way. filter doesn't pass index as an argument to greaterThanTen, but rather the element at that index.
Another thing: I don't know if you understand that filter only returns one 'category' as output -- you can only sort one condition at a time. Also, filter doesn't operate on the original list, but creates a new sequence, so filter(greaterThanTen, the_list) doesn't actually change anything. What you should do is: the_list = filter(greaterThanTen, the_list).
If you want to sort by the value at index 1 for each element in the list, you can do this:
filter(lambda element: yourSortFunction(elmenet[1]), list)
This is similar to the function you're using as a key to sort.
Another another thing: Why are you trying to call the_list in greaterThanTen, it makes no sense. The function stops evaluating code after the return statement.
Printing:
If you want to print a value from a specific index in a list just ask for that index.
print(LIST[index])
I hope this helps.
If you want to pass index as argument and maintain some flexibility of your list (you may have population at index other than 1), you can do this
def greaterThanTen(index):
return lambda x: x[index] > 10
def lessThanTen(index):
return lambda x: x[index] < 10
def myfilter(f, L):
return [x[0] for x in filter(f, L)]
print(myfilter(greaterThanTen(1), the_list)) # -> ['Georgia', 'Florida']
print(myfilter(lessThanTen(1), the_list)) # -> ['Alabama']
Or more generically,
import operator
def index_vs_num(index):
ops = {
'>' : operator.gt,
'<' : operator.lt,
'>=': operator.ge,
'<=': operator.le,
'=' : operator.eq
}
return lambda relation: lambda num: lambda x: ops[relation](x[index], num)
greaterThanTwenty = index_vs_num(1)('>')(20)
# the 1st argument is index of your population in the list
# the 2nd argument is the type of comparation
# the 3rd argument is the number to be compared
lessThanFive = index_vs_num(1)('<')(5)
def filter_by_outindex(*index):
def filter_by_f(f, L):
try:
return [x[index[0]] for x in filter(f, L)]
except IndexError:
return list(filter(f, L))
return filter_by_f
myfilter=filter_by_outindex(0)
print(myfilter(greaterThanTwenty, the_list)) # -> ['Florida']
print(myfilter(lessThanFive, the_list)) # -> ['Alabama']
I think this is what you actually want to achieve.
You could probably just do some sort of list comprehension and avoid filtering altogether.
the_final_list = [x for x in the_list if x[1] < 10]
This to me is simpler/more readable and accomplishes your objective.

Printing tuple indexed [:1] works, but [0] produces TypeError

fhand = open(raw_input('Enter a file name: '))
counts = dict()
words = []
for lines in fhand:
if lines.startswith('From') and not lines.startswith('From:'):
words = lines.split()
if words[1] not in counts:
counts[words[1]] = 1
else:
counts[words[1]] += 1
lst = list()
for key, val in counts.items():
lst.append((val, key))
lst.sort(reverse=True)
for key, val in lst[0]:
print key, val
The part in question is:
for key, val in lst[0]:
print key, val
This gives me: TypeError: 'int' object is not iterable. I have figured out that for some reason this does work:
input:
for key, val in lst[:1]:
print key, val
output:
Enter a file name: mbox-short.txt
5 cwen#iupui.edu
I'm just trying to figure out why exactly this works but not [0]. And as always, thank you for the help!
You have to realize the difference between [0] and [:1]
What [0] does is return to you the value that is stored at index 0 of the list.
On the other hand, when you do [:1], you are slicing the list, meaning you're getting all the elements of the list with index from 0 (inclusive) up to 1 (non inclusive).
From the documentation:
All slice operations return a new list containing the requested elements.
So when you slice a list, you're also getting back a list. And lists, being iterable types, can be -well- iterated! And this is true even if they contain only one element.
When you do for key, val in lst[0], you're trying to iterate a single value, the one that is stored at index 0 in lst. However, when instead of [0] you do [:1], what is returned isn't a single value but a range of values. In your case it may contain only one value, but it is still a range and therefore can be iterated in a for loop.
For a simpler explanation, see this great answer by Greg Hewgill on how list slices work.
Consider something like:
lst = [1, 2, 3]
lst[0] is an int (1) and an int is not iterable. However, lst[:1] is another list and therefore it is iterable.
In your case, you have a list of 2-tuples. so lst[0] is a 2-tuple -- Which is iterable. However, iterating over the 2-tuple will give you individual items which are not iterable and therefore cannot be unpacked into key, val. On the flip side, lst[:1] gives you a new list that holds 2-tuples. The list can be iterated and then the 2-tuples can be unpacked into key/value pairs.
list[0] returns an object, the element at index 0, but the for in loop syntax expects something iterable like a list of objects.
list[:1] slices the list into a new list that just includes the first element and so it will work.

how to convert a set in python into a dictionary

I am new to python and trying to convert a Set into a Dictionary. I am struggling to find a way to make this possible. Any inputs are highly appreciated. Thanks.
Input : {'1438789225', '1438789230'}
Output : {'1438789225':1, '1438789230':2}
Use enumerate() to generate a value starting from 0 and counting upward for each item in the dictionary, and then assign it in a comprehension:
input_set = {'1438789225', '1438789230'}
output_dict = {item:val for val,item in enumerate(input_set)}
Or a traditional loop:
output_dict = {}
for val,item in enumerate(input_set):
output_dict[item] = val
If you want it to start from 1 instead of 0, use item:val+1 for the first snippet and output_dict[item] = val+1 for the second snippet.
That said, this dictionary would be pretty much the same as a list:
output = list(input_set)
My one-liner:
output = dict(zip(input_set, range(1, len(s) + 1)))
zip mixes two lists (or sets) element by element (l1[0] + l2[0] + l1[1] + l2[1] + ...).
We're feeding it two things:
the input_set
a list from 1 to the length of the set + 1 (since you specified you wanted to count from 1 onwards, not from 0)
The output is a list of tuples like [('1438789225', 1), ('1438789230', 2)] which can be turned into a dict simply by feeding it to the dict constructor... dict.
But like TigerhawkT3 said, I can hardly find a use for such a dictionary. But if you have your motives there you have another way of doing it. If you take away anything from this post let it be the existence of zip.
an easy way of doing this is by iterating on the set, and populating the result dictionary element by element, using a counter as dictionary key:
def setToIndexedDict(s):
counter = 1
result = dict()
for element in s:
result[element] = counter #adding new element to dictionary
counter += 1 #incrementing dictionary key
return result
My Python is pretty rusty, but this should do it:
def indexedDict(oldSet):
dic = {}
for elem,n in zip(oldSet, range(len(oldSet)):
dic[elem] = n
return dic
If I wrote anything illegal, tell me and I'll fix it. I don't have an interpreter handy.
Basically, I'm just zipping the list with a range object (basically a continuous list of numbers, but more efficient), then using the resulting tuples.
Id got with Tiger's answer, this is basically a more naive version of his.

Categories