0

I have a list of integers ordered from maximum to minimum, like this: [2345,67,24,24,11,11,6,6,6,6,6,3,3,3,3,3,1,1,1,1,1,1]

I just simply want to calculate the portion of each value in this list, like 5% is '1', 4% is '3' ,1% is 2345 and print out this result

What's an easy way to do this?

mtrw
  • 34,200
  • 7
  • 63
  • 71
manxing
  • 3,165
  • 12
  • 45
  • 56

4 Answers4

3

One way. I'm sure there will be better ways to do it.

   import collections
   d = collections.defaultdict(float)

   ip = [2345,67,24,24,11,11,6,6,6,6,6,3,3,3,3,3,1,1,1,1,1,1]
   length = len(ip)

   for i in ip:
       d[i] += 1

   for i in d:
       print "%5d : %.2f%%" % (i, (d[i]/length) * 100)
Noufal Ibrahim
  • 71,383
  • 13
  • 135
  • 169
  • This solution also doesn't match the expected output from the OP... but its nice! :) – mac Dec 06 '11 at 12:15
  • It is difficult to match the expected output from the OP, given that the percentages he gives do not correspond to the example. – rodrigo Dec 06 '11 at 12:17
  • @rodrigo - You can't exclude that you misunderstood what the OP wants, and his figures correspond - in fact - to the example... with the right computation applied. – mac Dec 06 '11 at 12:20
  • @mac Maybe... but I'd bet that he simple gave approximate values to the percentages, because if he knew the actual values, he wouldn't need to ask in the first place! – rodrigo Dec 06 '11 at 12:33
  • The question title is "simple calculation in python", not "how to format percentage output". I'm pretty sure the important part is the algorithm to calculate percentage composition of an ordered list. – machine yearning Dec 06 '11 at 12:37
2

This solution takes advantage of the fact that your elements are already ordered, and only makes a single pass through your original list (and a constant number of passes through the remaining data structures.

>>> from itertools import groupby
>>> x = [2345,67,24,24,11,11,6,6,6,6,6,3,3,3,3,3,1,1,1,1,1,1]
>>> grouped_x = [(k, sum(1 for i in g)) for k,g in groupby(x)]
>>> grouped_x
[(2345, 1), (67, 1), (24, 2), (11, 2), (6, 5), (3, 5), (1, 6)]

The groupby expression is borrowed from the first question I ever asked on SO, and basically just groups each contiguous block of the same value into a list of (value, instance generator) pairs. Then the outer list comprehension just converts the instance generators into their total length.

I think OP's figures were not accurate, but this seems to be something like what he was getting at. I took the ceiling function, but you could also round

>>> from math import ceil
>>> for k, v in grouped_x:
print int(ceil(100 * v / float(len(x)))),
print  "% is", k


5 % is 2345
5 % is 67
10 % is 24
10 % is 11
23 % is 6
23 % is 3
28 % is 1
Community
  • 1
  • 1
machine yearning
  • 9,889
  • 5
  • 38
  • 51
0

Not a simple function, but a bit of list comprehension:

x = [2345,67,24,24,11,11,6,6,6,6,6,3,3,3,3,3,1,1,1,1,1,1]
print [(i,x.count(i) * 100 / len(x)) for i in set(x)]

Will print

[(1, 27), (67, 4), (6, 22), (2345, 4), (11, 9), (3, 22), (24, 9)]

That are pairs of element / percent.

rodrigo
  • 94,151
  • 12
  • 143
  • 190
  • ...with the only problem none of your values are the one expected by the OP! :( – mac Dec 06 '11 at 12:10
  • Details, details... There are more efficient algorithms, but I was looking for a one-liner. Being ordered, it is easy to solve in O(n). – rodrigo Dec 06 '11 at 12:14
0
from collections import Counter, OrderedDict

items_count = len(x)

percentage_tuples = map(lambda tup: (tup[0], 100 * float(tup[1]) / items_count),
    Counter(x).most_common())
percentage_dict = OrderedDict(percentage_tuples)

percentage_dict will be ordered by portion from high to lower.

to print it out:

for item in percentage_tuples:
    print("%d%% is '%s'" % (item[1], item[0]))
lig
  • 3,567
  • 1
  • 24
  • 36
  • 1
    This is unreadable, and would send most users to the man pages for about 3 hours. – machine yearning Dec 06 '11 at 12:39
  • what line seems unreadable for you? there are 2 collections imported and only their methods are used. the map with lambda could be a little bit unreadable but after looking closely it is nice enough this code is for learning how to make things. i suggest to write a subclass of the Counter collection to do all calculations – lig Dec 06 '11 at 13:25
  • 1
    I personally don't like the use of `lambda` and `map`. That line is longer than I'd prefer. – Noufal Ibrahim Dec 06 '11 at 13:27
  • @NoufalIbrahim Sorry, but then you proubably don't like to read Python code at all. – lig Dec 06 '11 at 13:29
  • 1
    I do. I generally prefer using genexps to using map. Your use of the collections is elegant but I'd have written that line like this `percentage_tuples = ((i[0], 100*float(i[1])/items_count) for i in Counter(x).most_common())`. It would probably run faster and have better memory characteristics for large lists as well. – Noufal Ibrahim Dec 06 '11 at 13:44
  • @lig: Sorry, I didn't mean to offend you, but Noufal has a point. I think it's fairly evident that the OP has little experience in python, so if you insist on referring to two different `Collections`, I'd suggest breaking it up into two lines and explaining in between each step what's going on. And the functional paradigm, although I use it daily, is not best used to stretch a single expression into an epic poem. Expressions should be kept short and sweet if at all possible. Nesting should be kept to 2 or 3 levels at most. If so, the functional paradigm can actually increase code readability. – machine yearning Dec 06 '11 at 13:47
  • @machineyearning i feel sense in your words – lig Dec 09 '11 at 14:04
  • 1
    @NoufalIbrahim just one thing: maps are faster than comprehensions. test it yourself. but lambda could decrease performance here…;) – lig Dec 09 '11 at 14:05
  • @lig: You're right. map runs faster than list comprehensions and genexps for inbuilt functions (I used `int`). The lambda is a killer though like you said. Genexps have one advantage (though not useful here) which is that they generate values on demand rather than build it upfront. Thanks! – Noufal Ibrahim Dec 09 '11 at 14:16