Find second maximum number in a list [duplicate] - python

This question already has answers here:
How to remove items from a list while iterating?
(25 answers)
Closed 12 months ago.
First line contains N. Second line contains list of N integers each separated by a space. I need to find the second largest number in list.
My code:
N = int(raw_input())
L = map(int, raw_input().split())
for i in L:
if i == max(L):
L.remove(i)
print L
print max(L)
If input is [2, 6, 9, 9, 5], this still prints maximum value: 9, as only one 9 is getting removed from the list.
So, how to remove all the 1st maximum values in the list?

The reason that it's still returning 9 is because you're mutating the list as you iterate over it. Essentially the steps are:
1. 2 6 9 9 5
^idx0
2. 2 6 9 9 5
^idx1
3. 2 6 9 9 5
^idx2
3a. 2 6 9 5
^idx2
4. 2 6 9 5
^
See how it skips evaluating the second 9 when it deletes the first 9? You'll notice if your list is [2, 6, 9, 5, 9], it works appropriately.
The minimal change to your code that will make it function is to iterate over a copy of the list, rather than the list itself.
L = [2, 6, 9, 9, 5]
maxL = max(L) # gotta move this out!!
for i in L[:]: # the slice creates a copy
if i == maxL:
L.remove(i)
print(max(L))
However it's probably easier to make a set (ensuring uniqueness), sort it, and return the second-to-last entry.
second_max = sorted(set(L))[-2]

try
N = 5
L = map(int, "2 6 9 9 5".split())
maxL = max(L)
#list comprehension for remove all occurrences of max(L)
L_filter = [e for e in L if e!=maxL]
print L
#print max of L_filter, second maximum of L
print max(L_filter)
you get:
[2, 6, 9, 9, 5]
6

Remove duplicated elements by converting to a set:
values = set(L)
Then remove the maximum:
values.discard(max(values))

you could also do this
L = [2, 6, 9, 9, 5]
L_tuples = zip(L, range(len(L))) #need tuples to make a dict
L_map = dict(L_tuples) #use the dict to dedupe
L_uniq = L_map.keys() #get back deduped values
L_sorted = sorted(L_uniq) #sort them ascending
second_largest = L_sorted[-2] #second from last is second largest
#or, rolling all that up...
second_largest = sorted(dict(zip(L, range(len(L)))).keys())[-2]

>>> import heapq
>>> values = [2, 6, 9, 9, 5]
>>> heapq.heapify(values)
>>> heapq._heapify_max(values)
>>> value = top = heapq.heappop()
>>> while value == top:
... value = heapq.heappop()
>>> print value

Here is another way to calculate the second maximum in a list. The code also considers the scenario that includes duplicate elements in the list.
number_of_elements=int(input('The number of elements in list\n'))
a=[]
for i in range(number_of_elements):
a.append(int(input('enter the list elements')))
#Use built-in function to calculate the maximum
max_list=max(a)
print("The maximum element is ",max_list)
#Calculate the number of times the number occur
count_of_max_num=a.count(max_list)
b=a
if (count_of_max_num == 1):
b.remove(max_list)
second_max=max(b)
print("The second largest number is", second_max, "The new list is" ,b)
else:
for i in range(count_of_max_num):
b.remove(max_list)
print ("The second largest is" , max(b))

Related

Find 3 maximum elements of list with python

How can I search for the three maximum elements of a list and replace it at same index with its result when divided by 2.
Please what am I doing wrong:
input is: 2 5 8 19 1 15 7 20 11
output should be : 2 5 8 9.5 1 7.5 7 10 11
Index out of range is the result displayed
def numberInput(line):
lineInput = [int(i) for i in line.split()]
min1 = min(lineInput)
for j in range(len(lineInput)):
if lineInput[j]>min1 and lineInput[j] > lineInput[j+1]:
max1 = lineInput[j]/float(2)
else:
max1 = lineInput[j]/float(2)
lineInput[j] = max1
lineInput[j] = max1
return(lineInput)
number = '2 5 8 19 1 15 7 20 11'
print(numberInput(number))
If the order of list isn't important, you can simply sort the list in descending order and then replace the first 3 elements as
a = [2, 5, 8, 19, 1, 15, 7, 20, 11]
a.sort(reverse = True)
a[0] = a[0] / 2
a[1] = a[1] / 2
a[2] = a[2] / 2
Output
[10.0, 9.5, 7.5, 11, 8, 7, 5, 2, 1]
If the order is important,
import heapq
largest = heapq.nlargest(3, a)
for i in range(len(a)):
if a[i] in largest:
a[i] = a[i] / 2
Output
[2, 5, 8, 9.5, 1, 7.5, 7, 10.0, 11]
heapq.nlargest() is a function of heapq which can give you n largest numbers from a list. Since lists in Python do not have a replace function, the list had to be traversed once, and then replaced manually. Hope this resolves your issue.
Why wouldn't you just walk through the list once (it maybe a bit longer code, but definitely efficient)
def numberInput(line):
lineInput = [int(i) for i in line.split()]
n = len(lineInput)
if n <= 3:
return [i / 2 for i in lineInput]
max_pairs = [(lineInput[i], i) for i in range(3)] # keep track of 3 max's and their indices
max_pairs.sort(key = lambda x: -x) # sort in descending order
for i in range(3, n):
if lineInput[i] >= max_pairs[0][0]: # greater than the largest element
max_pairs = [(lineInput[i], i)] + max_pairs[:2]
elif lineInput[i] >= max_pairs[1][0]: # greater than second element
max_pairs = [max_pairs[0], (lineInput[i], i), max_pairs[1]]
elif lineInput[i] >= max_pairs[2][0]: # greater than third element
max_pairs = max_pairs[:2] + [(lineInput[i], i)]
for pair in max_pairs:
lineInput[pair[1]] = lineInput[pair[0]] / 2
return lineInput
Explanation: max_pairs is a set of three tuples, containing the maximum three elements and their indices
Note: I think the above is easiest to understand, but you can do it in a loop if you don't like all those ifs

How can I sum every n array values and place the result into a new array? [duplicate]

This question already has answers here:
Sum slices of consecutive values in a NumPy array
(5 answers)
Closed 3 years ago.
I have a very long list of array numbers I would like to sum and place into a new array. For example the array:
[1,2,3,4,5,6,7,8,1,2,3,4,5,6,7,8]
would become:
[6,15,16,6,15,x]
if I was to sum every 3.
I cannot figure out how to go about it. I think possibly one problem is I do not know the length of my array - I do not mind losing the bottom bit of data if necessary.
I have tried the numpy.reshape function with no success:
x_ave = numpy.mean(x.reshape(-1,5), axis=1)
ret = umr_sum(arr, axis, dtype, out, keepdims)
I get an error:
TypeError: cannot perform reduce with flexible type
Cut the array to the correct length first then do a reshape.
import numpy as np
N = 3
a = np.array([1,2,3,4,5,6,7,8,1,2,3,4,5,6,7,8])
# first cut it so that lenght of a % N is zero
rest = a.shape[0]%N
a = a[:-rest]
assert a.shape[0]%N == 0
# do the reshape
a_RS = a.reshape(-1,N)
print(a_RS)
>> [[1 2 3]
[4 5 6]
[7 8 1]
[2 3 4]
[5 6 7]]
then you can simply add it up:
print(np.sum(a_RS,axis=1))
>> [ 6 15 16 9 18]
You can use a list comprehension do this:
ls = [1,2,3,4,5,6,7,8,1,2,3,4,5,6,7,8]
res = [sum(ls[i:i+3]) for i in range(0, len(ls), 3)]
[6, 15, 16, 9, 18, 8]
This will result in all the numbers being included in the resulting sum. If you don't want this to happen, then you can just check for it and replace the last sum with whatever value you want:
if (len(ls)%3) != 0:
res[-1] = 'x'
[6, 15, 16, 9, 18, 'x']
Or remove it entirely:
if (len(ls)%3) != 0:
res[:] = res[:-1]
[6, 15, 16, 9, 18]
Why don't you just simply use a list comprehension? E.g.
my_list = [1,2,3,4,5,6,7,8,1,2,3,4,5,6,7,8]
len_list = len(my_list) - len(my_list) % 3 # ignore end of list, s.t., only tuples of three are considered
[my_list[i] + my_list[i+1] + my_list[i+2] for i in range(0, len_list, 3)]

Adding to a specific variable in a list

I'm trying to add two lists. If the last variable is greater than 10, it needs to carry over to the previous variable in the list. For example :
1 / 2 / 3 (List 1)
7 / 8 / 9 (List 2)
Should equal
9 / 1 / 2 not 8/10/12
So far, I have
list1 = [1, 2, 3]
list2 = [7, 8, 9]
SumOfLists = [x+y for x,y in zip(list1, list2)]
That adds the lists together, but I'm not sure how to make the number carry over.
You can try this code.
list1 = [1, 2, 3]
list2 = [7, 8, 9]
def add_list(a,b):
carry = 0
res_list = []
for i,j in zip(a[::-1],b[::-1]): # Iterate through the lists in reverse
val = (i+j+carry)%10 # Store the sum in val
carry = (i+j+carry)//10 # Store the carry
res_list.append(val) # Append to the returning list
return res_list[::-1] # Return the list
print add_list(list1,list2)
Wil print
[9, 1, 2]
Algorithm
Loop through each of the values in reverse. Add each corresponding values. If the values are above 10 then find the exceeding value and put it to carry. Finally return the reverse of the list.
list1 = [1, 2, 3]
list2 = [7, 8, 9]
cur = 0 # num to carry over
result = []
for x,y in zip(reversed(list2),reversed(list1)):
if x + y + cur > 10: # if sum greater than 10, remember to add 1 on
t = x+y + cur # the next loop
d = str(t)[1] # get the rightmost digit
result.append(int(d))
cur = 1
else: # nothing to curry over, but still add cur,
# it may be 1
result.append(x+y+cur)
cur = 0
print(list(reversed(result)) )
[9, 1, 2]
just subtract 10 if it's more then 10 and add 1 to it's previous element. Do this proccess for all element in sum list
if SumOfLists[2] >= 10:
SumOfLists[2] -= 10
SumOfLists[1] += 1
And at last check
if SumOfLists[0] >= 10:
for i in range(len(SumOfLists)-1,0,-1):
SumOfLists[i] = SumOfLists[i-1]
SumOfLists[0] = 1

How to limit for loop to print first few element from list in terms of their value in python?

I would like to limit for loop to print first few element from list in terms of their value. For example, if i < 6 :
list = [1,2,3,4,5,6,7,8,9,10]
for i < 6 in list:
print(i)
Thanks in advance !
In [9]: L = [1,2,3,4,5,6,7,8,9,10]
In [10]: for i in L:
....: if i<6:
....: print(i)
....:
1
2
3
4
5
based on I would like to limit for loop to print first few element from list in terms of their value it seems the list is in order so you can use itertools.takewhile :
from itertools import takewhile
lst = [1,2,3,4,5,6,7,8,9,10] # don't use list
tke = takewhile(lambda x: x< 6, lst)
for t in tke:
print(t)
1
2
3
4
5
If you want a list use list(...).
print(list(takewhile(lambda x: x< 6, lst))) # good reason why we should not use list as a variable name
[1, 2, 3, 4, 5]

Loop problem while iterating through a list and removing recurring elements [duplicate]

This question already has answers here:
Modifying list while iterating [duplicate]
(7 answers)
Closed 8 years ago.
I want to iterate through a list, and remove the items that count more than once, so they don't get printed repeatedly by the for loop.
However, some items appearing only one time in the list seem to get affected too by this, and I can't figure out why.
Any input would be greatly appreciated.
Example Output:
listy = [2,2,1,3,4,2,1,2,3,4,5]
for i in listy:
if listy.count(i)>1:
print i, listy.count(i)
while i in listy: listy.remove(i)
else:
print i, listy.count(i)
Outputs:
2 4
3 2
1 2
thus ignoring completely 4 and 5.
You should not modify a list while iterating over it. This one should work:
listy = [2,2,1,3,4,2,1,2,3,4,5]
found = set()
for i in listy:
if not i in found:
print i, listy.count(i)
found.add(i)
The result is:
2 4
1 2
3 2
4 2
5 1
The reason for your problems is that you modify the list while you are iterating over it.
If you don't care about the order in which items appear in the output and don't care about the count, you can simply use use a set:
>>> listy = [2,2,1,3,4,2,1,2,3,4,5]
>>> print set(listy)
set([1, 2, 3, 4, 5])
If you do care about the count, use the Counter class from the collections module in the Standard Library:
>>> import collections
>>> collections.Counter(listy)
Counter({2: 4, 1: 2, 3: 2, 4: 2, 5: 1})
>>> c = collections.Counter(listy)
>>> for item in c.iteritems():
... print "%i has a count of %i" % item
...
1 has a count of 2
2 has a count of 4
3 has a count of 2
4 has a count of 2
5 has a count of 1
If you do care about both the order and the count, you have to build a second list:
>>> checked = []
>>> counts = []
>>> for item in listy:
>>> if item not in checked:
>>> checked.append(item)
>>> counts.append(listy.count(item))
>>> print zip(checked, counts)
... [(2, 4), (1, 2), (3, 2), (4, 2), (5, 1)]
This is the least efficient solution, of course.
If you don't want to keep the counts for later, you don't need the counts list:
listy = [2,2,1,3,4,2,1,2,3,4,5]
checked = set()
for item in listy:
# "continue early" looks better when there is lots of code for
# handling the other case
if item in checked:
continue
checked.add(item)
print item, listy.count(item)
Don't modify a list while iterating over it, it will mess you up every time:
listy = [2,2,1,3,4,2,1,2,3,4,5]
# * * * Get hit
for i in listy:
print i
if listy.count(i) > 1:
print i, listy.count(i), 'item and occurences'
while i in listy: listy.remove(i)
else:
print i, listy.count(i)
First, you remove four 2s. Two are right at the beginning, so that puts you at the first 1.
Then you advance one when you get the next i from listy, putting you at the first 3.
Then you remove two 3s. The first is right there, so that puts you at the first 4.
Then you advance one again. The 2 is gone already, so this puts you at the second 1.
You then delete both 1s; this moves you forward two spaces. The 2 and 3 are gone, so this puts you at the 5.
You advance one, this moves you off the end of the list so the loop is over.
If what you want is to print each item only once, you can use the simple set method, or you could use the itertools unique_everseen recipe:
def unique_everseen(iterable, key=None):
"List unique elements, preserving order. Remember all elements ever seen."
# unique_everseen('AAAABBBCCDAABBB') --> A B C D
# unique_everseen('ABBCcAD', str.lower) --> A B C D
seen = set()
seen_add = seen.add
if key is None:
for element in ifilterfalse(seen.__contains__, iterable):
seen_add(element)
yield element
else:
for element in iterable:
k = key(element)
if k not in seen:
seen_add(k)
yield element
Which extends the basic set version to allow you to specify a special way to compare items.
If you want to know which items are only in the list once:
listy2 = filter(lambda i: listy.count(i) == 1, listy)
listy2 now has all the single occurrences.
If you don't like the lambda, just do:
def getsingles(listy):
def singles(i):
return listy.count(i) == 1
return singles
then:
listy2 = filter(getsingles(listy), listy)
This makes a special function that will tell you which items are in listy only once.
The reason of the behavior you get is here, in the note:
http://docs.python.org/reference/compound_stmts.html#index-811
Update 1
agf's solution isn't a good one for performance reason: the list is filtered according to the count of each element. The counting is done for each element, that is to say the counting process that consists to run through the entire list to count, is done as many times as there are elements in list: it's overconsuming time, imagine if your list is 1000 length
A better solution I think is to use an instance of Counter:
import random
from collections import Counter
li = [ random.randint(0,20) for i in xrange(30)]
c = Counter(li)
print c
print type(c)
res = [ k for k in c if c[k]==1]
print res
result
Counter({8: 5, 0: 3, 4: 3, 9: 3, 2: 2, 5: 2, 11: 2, 3: 1, 6: 1, 10: 1, 12: 1, 15: 1, 16: 1, 17: 1, 18: 1, 19: 1, 20: 1})
<class 'collections.Counter'>
[3, 6, 10, 12, 15, 16, 17, 18, 19, 20]
Another solution would be to add the read elements in a set in order that the program avoids to make a count for an already seen element.
Update 2
errrr.... my solution is stupid, you don't want to select the element appearing only one time in the list....
Then the following code is the right one , I think:
import random
from collections import Counter
listy = [ random.randint(0,20) for i in xrange(30)]
print 'listy==',listy
print
c = Counter(listy)
print c
print type(c)
print
slimmed_listy = []
for el in listy:
if el in c:
slimmed_listy.append(el)
print 'element',el,' count ==',c[el]
del c[el]
print
print 'slimmed_listy==',slimmed_listy
result
listy== [13, 10, 1, 1, 13, 11, 18, 15, 3, 15, 12, 11, 15, 18, 11, 10, 14, 10, 20, 3, 18, 9, 11, 2, 19, 15, 5, 14, 1, 1]
Counter({1: 4, 11: 4, 15: 4, 10: 3, 18: 3, 3: 2, 13: 2, 14: 2, 2: 1, 5: 1, 9: 1, 12: 1, 19: 1, 20: 1})
<class 'collections.Counter'>
element 13 count == 2
element 10 count == 3
element 1 count == 4
element 11 count == 4
element 18 count == 3
element 15 count == 4
element 3 count == 2
element 12 count == 1
element 14 count == 2
element 20 count == 1
element 9 count == 1
element 2 count == 1
element 19 count == 1
element 5 count == 1
slimmed_listy== [13, 10, 1, 11, 18, 15, 3, 12, 14, 20, 9, 2, 19, 5]
In case you wouldn't want the result in the order of listy, the code would be even simpler
Update 3
If you want only to print, then I propose:
import random
from collections import Counter
listy = [ random.randint(0,20) for i in xrange(30)]
print 'listy==',listy
print
def gener(li):
c = Counter(li)
for el in li:
if el in c:
yield el,c[el]
del c[el]
print '\n'.join('element %4s count %4s' % x for x in gener(listy))
result
listy== [16, 2, 4, 9, 15, 19, 1, 1, 3, 5, 12, 15, 12, 3, 17, 13, 8, 11, 4, 6, 15, 1, 0, 1, 3, 3, 6, 5, 0, 8]
element 16 count 1
element 2 count 1
element 4 count 2
element 9 count 1
element 15 count 3
element 19 count 1
element 1 count 4
element 3 count 4
element 5 count 2
element 12 count 2
element 17 count 1
element 13 count 1
element 8 count 2
element 11 count 1
element 6 count 2
element 0 count 2
Modifying a list while you iterate over it is a bad idea in every language I have encountered. My suggestion: don't do that. Here are some better ideas.
Use a set to find single occurrences
source = [2,2,1,3,4,2,1,2,3,4,5]
for s in set(source):
print s
And you get this:
>>> source = [2,2,1,3,4,2,1,2,3,4,5]
>>> for s in set(source):
... print s
...
1
2
3
4
5
If you want the counts, use defaultdict
from collections import defaultdict
d = defaultdict(int)
source = [2,2,1,3,4,2,1,2,3,4,5]
for s in source:
d[s] += 1
for k, v in d.iteritems():
print k, v
You'll get this:
>>> for k, v in d.iteritems():
... print k, v
...
1 2
2 4
3 2
4 2
5 1
If you want your results sorted, use sort and operator
import operator
for k, v in sorted(d.iteritems(), key=operator.itemgetter(1)):
print k, v
You'll get this:
>>> import operator
>>> for k, v in sorted(d.iteritems(), key=operator.itemgetter(1)):
... print k, v
...
5 1
1 2
3 2
4 2
2 4
I am not sure if it is a good idea to iterate the list and remove elements at the same time. If you really just want to output all items and their number of occurrences, I would do it like this:
listy = [2,2,1,3,4,2,1,2,3,4,5]
listx = []
listc = []
for i in listy:
if not i in listx:
listx += [i]
listc += [listy.count(i)]
for x, c in zip(listx, listc):
print x, c
Like agf said, modifying a list while you iterate it will cause problems. You could solve your code by using while and pop:
single_occurrences = []
while listy:
i = listy.pop(0)
count = listy.count(i)+1
if count > 1:
print i, count
while i in listy: listy.remove(i)
else:
print i, count
single_occurrences.append(i)
Output:
2 4
1 2
3 2
4 2
5 1
One way to do that would be to create a result list and test whether the tested value is in it :
res=[]
listy = [2,2,1,3,4,2,1,2,3,4,5]
for i in listy:
if listy.count(i)>1 and i not in res:
res.append(i)
for i in res:
print i, listy.count(i)
Result :
2 4
1 2
3 2
4 2

Categories

Resources