0

Here's a frequency table implemented as a list.

table = [1,3,2]

The desired output is a list of individual values.

unspooled =  [0, 1, 1, 1, 2, 2]

The following syntax will get the job done.

sum((freq*[score] for score, freq in enumerate(table)), []) 

As an exercise to test my understanding, however, I'd like to know if there's a way to accomplish this with pure list comprehension. Thank you.

(Or secondarily, if there's a way to do it that is more expressive than what I have that isn't pure list comprehension, I'm open to that, too.)

PS Apparently, my syntax yields terrible performance.

Update. Timings of all the suggested solutions.

In [9]: table
Out[9]: range(0, 1000)

In [10]: %timeit [i for i, x in enumerate(table) for _ in range(x)]
10 loops, best of 3: 27.1 ms per loop

In [11]: %timeit [ind for ind in range(len(table)) for val in range(table[ind])]
10 loops, best of 3: 27 ms per loop

In [12]: %timeit reduce( lambda x,y:x+y, [ [i]*frq for i,frq in enumerate(table)] )
1 loop, best of 3: 1.11 s per loop

In [13]: %timeit list(itertools.chain(freq*[score] for score, freq in enumerate(table))
100 loops, best of 3: 3.84 ms per loop
Community
  • 1
  • 1
bongbang
  • 1,610
  • 4
  • 18
  • 34

3 Answers3

2

I believe this would work:

unspooled = [ind for ind in range(len(table)) for val in range(table[ind])]
mitoRibo
  • 4,468
  • 1
  • 13
  • 22
  • Wow, that's a lot less expressive than what I have. It does work, but frankly, I have no idea why. When and how does `val` get used? – bongbang Jul 14 '16 at 02:07
  • I see now. `val` doesn't get used. It's just a throwaway to get going the loop that operates on `ind`. – bongbang Jul 14 '16 at 03:18
2

You can use enumerate to return the index and item from table:

>>> table = [1,3,2]
>>> [i for i, x in enumerate(table) for _ in range(x)]
[0, 1, 1, 1, 2, 2]
niemmi
  • 17,113
  • 7
  • 35
  • 42
0
table = [1,3,2]
res = reduce( lambda x,y:x+y, [ [i]*frq for i,frq in enumerate(table)] )

output:
[0, 1, 1, 1, 2, 2]
galaxyan
  • 5,944
  • 2
  • 19
  • 43