2013-05-01 19 views
7

Update: beginnend mit Version 0,20.0, pandas cut/qcut behandelt Datumsfelder. Weitere Informationen finden Sie unter What's New.Was ist äquivalent zu cut/qcut für Pandas Datumsfelder?

pd.cut und pd.qcut jetzt datetime64 und timedelta64 dtypes (GH14714, GH14798)

Original Frage unterstützen: Pandas Schnitt und qcut Funktionen sind für 'bucketing' kontinuierliche Daten für Verwenden Sie in Pivot-Tabellen und so weiter, aber ich sehe keine einfache Möglichkeit, um Datetime-Achsen in der Mischung zu erhalten. Frustrierend seit Pandas ist so toll in all den zeitbezogenen Sachen!

Hier ist ein einfaches Beispiel:

def randomDates(size, start=134e7, end=137e7): 
    return np.array(np.random.randint(start, end, size), dtype='datetime64[s]') 

df = pd.DataFrame({'ship' : randomDates(10), 'recd' : randomDates(10), 
        'qty' : np.random.randint(0,10,10), 'price' : 100*np.random.random(10)}) 
df 

    price  qty recd    ship 
0 14.723510 3 2012-11-30 19:32:27 2013-03-08 23:10:12 
1 53.535143 2 2012-07-25 14:26:45 2012-10-01 11:06:39 
2 85.278743 7 2012-12-07 22:24:20 2013-02-26 10:23:20 
3 35.940935 8 2013-04-18 13:49:43 2013-03-29 21:19:26 
4 54.218896 8 2013-01-03 09:00:15 2012-08-08 12:50:41 
5 61.404931 9 2013-02-10 19:36:54 2013-02-23 13:14:42 
6 28.917693 1 2012-12-13 02:56:40 2012-09-08 21:14:45 
7 88.440408 8 2013-04-04 22:54:55 2012-07-31 18:11:35 
8 77.329931 7 2012-11-23 00:49:26 2012-12-09 19:27:40 
9 46.540859 5 2013-03-13 11:37:59 2013-03-17 20:09:09 

Bin von Gruppen von Preis oder Menge, kann ich sie schneiden/qcut Eimer verwenden:

df.groupby([pd.cut(df['qty'], bins=[0,1,5,10]), pd.qcut(df['price'],q=3)]).count() 

         price qty recd ship 
qty  price    
(0, 1] [14.724, 46.541] 1 1 1 1 
(1, 5] [14.724, 46.541] 2 2 2 2 
     (46.541, 61.405] 1 1 1 1 
(5, 10] [14.724, 46.541] 1 1 1 1 
     (46.541, 61.405] 2 2 2 2 
     (61.405, 88.44] 3 3 3 3 

Aber ich jeden einfach nicht sehen kann Möglichkeit, das Gleiche mit meinen 'Recd' oder 'Ship' Datumsfeldern zu tun. Zum Beispiel, erzeugen Sie eine ähnliche Tabelle von Zählungen, die durch (sagen wir) monatliche Buckets von recd und ship unterteilt sind. Es sieht so aus, als ob resample() alle Maschinen in Perioden einteilen kann, aber ich kann nicht herausfinden, wie man es hier anwendet. Die Buckets (oder Level) im 'date cut' wären äquivalent zu einem pandas.PeriodIndex, und dann möchte ich jeden Wert von df ['recd'] mit der Periode versehen, in die er fällt?

So ist die Art der Ausgabe, die ich suche wäre so etwas wie:

ship recv  count 
2011-01 2011-01 1 
     2011-02 3 
     ...  ... 
2011-02 2011-01 2 
     2011-02 6 
...  ...  ... 

Generell würde Ich mag Lage sein, kontinuierlich oder kategorische Variablen in der Ausgabe zu mischen und anzupassen. Stellen Sie sich vor df auch eine Spalte ‚Status‘ mit rot/gelb/grün-Werte enthält, dann vielleicht möchte ich zählt zusammenzufassen nach Status, Preis Eimer, Schiffs- und Erh Eimer, so:

ship recv  price status count 
2011-01 2011-01 [0-10) green  1 
          red  4 
       [10-20) yellow  2 
        ...  ... ... 
     2011-02 [0-10) yellow  3 
     ...  ...  ... ... 

Als Bonus Frage, was ist Die einfachste Möglichkeit, das obige groupby() - Ergebnis so zu ändern, dass nur eine einzige Ausgabespalte namens "count" enthalten ist.

Antwort

4

Hier ist eine Lösung mit pandas.PeriodIndex (Vorbehalt: PeriodIndex nicht scheinen Zeitregeln mit einem Multiple> 1, wie '4M' zu unterstützen scheinen). Ich denke die Antwort auf Ihre Bonusfrage ist .size().

In [49]: df.groupby([pd.PeriodIndex(df.recd, freq='Q'), 
    ....:    pd.PeriodIndex(df.ship, freq='Q'), 
    ....:    pd.cut(df['qty'], bins=[0,5,10]), 
    ....:    pd.qcut(df['price'],q=2), 
    ....:   ]).size() 
Out[49]: 
       qty  price 
2012Q2 2013Q1 (0, 5] [2, 5] 1 
2012Q3 2013Q1 (5, 10] [2, 5] 1 
2012Q4 2012Q3 (5, 10] [2, 5] 1 
     2013Q1 (0, 5] [2, 5] 1 
       (5, 10] [2, 5] 1 
2013Q1 2012Q3 (0, 5] (5, 8] 1 
     2013Q1 (5, 10] (5, 8] 2 
2013Q2 2012Q4 (0, 5] (5, 8] 1 
     2013Q2 (0, 5] [2, 5] 1 
4

einfach den Index des Feldes gesetzt bräuchten Sie, hier einige Beispiele

In [36]: df.set_index('recd').resample('1M',how='sum') 
Out[36]: 
       price qty 
recd      
2012-07-31 64.151194 9 
2012-08-31 93.476665 7 
2012-09-30 94.193027 7 
2012-10-31   NaN NaN 
2012-11-30   NaN NaN 
2012-12-31 12.353405 6 
2013-01-31   NaN NaN 
2013-02-28 129.586697 7 
2013-03-31   NaN NaN 
2013-04-30   NaN NaN 
2013-05-31 211.979583 13 

In [37]: df.set_index('recd').resample('1M',how='count') 
Out[37]: 
2012-07-31 price 1 
      qty  1 
      ship  1 
2012-08-31 price 1 
      qty  1 
      ship  1 
2012-09-30 price 2 
      qty  2 
      ship  2 
2012-10-31 price 0 
      qty  0 
      ship  0 
2012-11-30 price 0 
      qty  0 
      ship  0 
2012-12-31 price 1 
      qty  1 
      ship  1 
2013-01-31 price 0 
      qty  0 
      ship  0 
2013-02-28 price 2 
      qty  2 
      ship  2 
2013-03-31 price 0 
      qty  0 
      ship  0 
2013-04-30 price 0 
      qty  0 
      ship  0 
2013-05-31 price 3 
      qty  3 
      ship  3 
dtype: int64 
+1

Dies scheint nicht wie eine allgemeine Lösung, z.B. wenn ich an zwei verschiedenen Daten oder einem Datum und einem Nicht-Datum gruppieren möchte (über Schnitt- oder Kategorievariable). Ich werde die Frage mit der Struktur der Ausgabe, die ich suche, aktualisieren. – patricksurry

0

Wie wäre es mit Series und setzen die Teile des DataFrame, die Sie in die interessiert sind, sampeln durch mögen, Rufen Sie dann auf dem Serienobjekt auf?

price_series = pd.Series(df.price.tolist(), index=df.recd) 

und dann

pd.qcut(price_series, q=3) 

und so weiter. (Obwohl ich denke @ Jeffs Antwort ist am besten)

1

Ich kam mit einer Idee, die auf dem zugrunde liegenden Speicherformat von datetime64 [ns] beruht.Wenn Sie definieren dcut() wie dieses

def dcut(dts, freq='d', right=True): 
    hi = pd.Period(dts.max(), freq=freq) + 1 # get first period past end of data 
    periods = pd.PeriodIndex(start=dts.min(), end=hi, freq=freq) 
    # get a list of integer bin boundaries representing ns-since-epoch 
    # note the extra period gives us the extra right-hand bin boundary we need 
    bounds = np.array(periods.to_timestamp(how='start'), dtype='int') 
    # bin our time field as integers 
    cut = pd.cut(np.array(dts, dtype='int'), bins=bounds, right=right) 
    # relabel the bins using the periods, omitting the extra one at the end 
    cut.levels = periods[:-1].format() 
    return cut 

Dann können wir das tun, was ich wollte:

df.groupby([dcut(df.recd, freq='m', right=False),dcut(df.ship, freq='m', right=False)]).count() 

Um:

   price qty recd ship 
2012-07 2012-10 1 1 1 1 
2012-11 2012-12 1 1 1 1 
     2013-03 1 1 1 1 
2012-12 2012-09 1 1 1 1 
     2013-02 1 1 1 1 
2013-01 2012-08 1 1 1 1 
2013-02 2013-02 1 1 1 1 
2013-03 2013-03 1 1 1 1 
2013-04 2012-07 1 1 1 1 
     2013-03 1 1 1 1 

Ich denke, man ähnlich dqcut definieren könnte(), die Zuerst wird jeder datetime-Wert mit der Ganzzahl "gerundet", die den Anfang seiner enthaltenden Periode darstellt (in der angegebenen Häufigkeit), und dann wird qcut() verwendet, um zwischen diesen Grenzen zu wählen. Oder machen Sie qcut() zuerst mit den rohen Ganzzahlwerten und runden Sie die resultierenden Bins auf Basis Ihrer gewählten Häufigkeit ab?

Noch keine Freude an der Bonusfrage? :)

Verwandte Themen