3

I have a dataframe ('frame') on which I want to aggregate by Country and Date:

aggregated=pd.DataFrame(frame.groupby(['Country','Date']).CaseID.count())

aggregated["Total duration"]=frame.groupby(['Country','Date']).Hours.sum()

aggregated["Mean duration"]=frame.groupby(['Country','Date']).Hours.mean()

I want to compute the above figures (total duration, mean duration, etc.) only for the positive 'Hours' numbers in 'frame'. How can I do that?

Thanks!

Sample "frame"

import pandas as pd
Line1 = {"Country": "USA", "Date":"01 jan", "Hours":4}
Line2 = {"Country": "USA", "Date":"01 jan", "Hours":3}
Line3 = {"Country": "USA", "Date":"01 jan", "Hours":-999}
Line4 = {"Country": "Japan", "Date":"01 jan", "Hours":3}
pd.DataFrame([Line1,Line2,Line3,Line4])
alko
  • 46,136
  • 12
  • 94
  • 102
Alexis Eggermont
  • 7,665
  • 24
  • 60
  • 93

2 Answers2

9

Not as elegant as above, but deals differently some corner cases. df stands for frame from original question.

>>> df.groupby(['Country','Date']).agg(lambda x: x[x>0].mean())
                Hours
Country Date
Japan   01 jan    3.0
USA     01 jan    3.5
>>> df.ix[3, 'Hours'] = -1
>>> df.groupby(['Country','Date']).agg(lambda x: x[x>0].mean())
                Hours
Country Date
Japan   01 jan    NaN
USA     01 jan    3.5
alko
  • 46,136
  • 12
  • 94
  • 102
  • A better approach would be to just use `NaN` as the sentinel value instead of `-999`, and then do no filtering at all and use `nanmean` or other `nan`-insensitive stats functions that have implicit, faster filtering already within them. But I realize you are taking the data as a given from the OP's question. – ely Dec 06 '13 at 19:36
  • One reason to prefer doing the filtering before the groupby is if you're reusing (e.g. sum, count etc.), my guess is will be faster to reuse (though perhaps less clear). – Andy Hayden Dec 06 '13 at 20:06
8

How about -

frame[frame["Hours"] > 0].groupby(['Country','Date'])
alko
  • 46,136
  • 12
  • 94
  • 102
kgu87
  • 2,050
  • 14
  • 12