Binning into timeslots - Is there a better way than using list comp?
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?
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.
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 n
references 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()), ...]
精彩评论