Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Binning into timeslots - Is there a better way than using list comp?

Tags:

python

I have a dataset of events (tweets to be specific) that I am trying to bin / discretize. The following code seems to work fine so far (assuming 100 bins):

HOUR = timedelta(hours=1)
start = datetime.datetime(2009,01,01)
z = [dt + x*HOUR for x in xrange(1, 100)]

But then, I came across this fateful line at python docs 'This makes possible an idiom for clustering a data series into n-length groups using zip(*[iter(s)]*n)'. The zip idiom does indeed work - but I can't understand how (what is the * operator for instance?). How could I use to make my code prettier? I'm guessing this means I should make a generator / iterable for time that yields the time in graduations of an HOUR?

like image 843
malangi Avatar asked Jun 10 '10 22:06

malangi


2 Answers

I will try to explain zip(*[iter(s)]*n) in terms of a simpler example:

imagine you have the list s = [1, 2, 3, 4, 5, 6]

iter(s) gives you a listiterator object that will yield the next number from s each time you ask for an element.

[iter(s)] * n gives you the list with iter(s) in it n times e.g. [iter(s)] * 2 = [<listiterator object>, <listiterator object>] - the key here is that these are 2 references to the same iterator object, not 2 distinct iterator objects.

zip takes a number of sequences and returns a list of tuples where each tuple contains the ith element from each of the sequences. e.g. zip([1,2], [3,4], [5,6]) = [(1, 3, 5), (2, 4, 6)] where (1, 3, 5) are the first elements from the parameters passed to zip and (2, 4, 6) are the second elements from the parameters passed to zip.

The * in front of *[iter(s)]*n converts the [iter(s)]*n from being a list into being multiple parameters being passed to zip. so if n is 2 we get zip(<listiterator object>, <listiterator object>)

zip will request the next element from each of its parameters but because these are both references to the same iterator this will result in (1, 2), it does the same again resulting in (3, 4) and again resulting in (5, 6) and then there are no more elements so it stops. Hence the result [(1, 2), (3, 4), (5, 6)]. This is the clustering a data series into n-length groups as mentioned.

like image 81
mikej Avatar answered Sep 21 '22 17:09

mikej


The expression from the docs looks like this:

zip(*[iter(s)]*n)

This is equivalent to:

it = iter(s)
zip(*[it, it, ..., it]) # n times

The [...]*n repeats the list n times, and this results in a list that contains nreferences to the same iterator.

This is again equal to:

it = iter(s)
zip(it, it, ..., it)    # turning a list into positional parameters

The * before the list turns the list elements into positional parameters of the function call.

Now, when zip is called, it starts from left to right to call the iterators to obtain elements that should be grouped together. Since all parameters refer to the same iterator, this yields the first n elements of the initial sequence. Then that process continues for the second group in the resulting list, and so on.

The result is the same as if you had constructed the list like this (evaluated from left to right):

it = iter(s)
[(it.next(), it.next(), ..., it.next()), (it.next(), it.next(), ..., it.next()),  ...]
like image 23
sth Avatar answered Sep 18 '22 17:09

sth