web-dev-qa-db-fra.com

Comment rééchantillonner une trame de données avec différentes fonctions appliquées à chaque colonne?

J'ai une série chronologique avec la température et le rayonnement dans un pandas dataframe. La résolution temporelle est de 1 minute par étapes régulières.

import datetime
import pandas as pd
import numpy as np

date_times = pd.date_range(datetime.datetime(2012, 4, 5, 8, 0),
                           datetime.datetime(2012, 4, 5, 12, 0),
                           freq='1min')
tamb = np.random.sample(date_times.size) * 10.0
radiation = np.random.sample(date_times.size) * 10.0
frame = pd.DataFrame(data={'tamb': tamb, 'radiation': radiation},
                     index=date_times)
frame
<class 'pandas.core.frame.DataFrame'>
DatetimeIndex: 241 entries, 2012-04-05 08:00:00 to 2012-04-05 12:00:00
Freq: T
Data columns:
radiation    241  non-null values
tamb         241  non-null values
dtypes: float64(2)

Comment puis-je sous-échantillonner ce dataframe à une résolution d'une heure, en calculant l'heure moyenne pour la température et l'heure somme pour le rayonnement?

35
bmu

Avec pandas 0,18 l'API de rééchantillonnage a changé (voir docs ). Donc pour pandas> = 0,18 la réponse est:

In [31]: frame.resample('1H').agg({'radiation': np.sum, 'tamb': np.mean})
Out[31]: 
                         tamb   radiation
2012-04-05 08:00:00  5.161235  279.507182
2012-04-05 09:00:00  4.968145  290.941073
2012-04-05 10:00:00  4.478531  317.678285
2012-04-05 11:00:00  4.706206  335.258633
2012-04-05 12:00:00  2.457873    8.655838

Ancienne réponse:

Je réponds à ma question pour refléter les changements liés aux séries chronologiques dans pandas >= 0.8 (toutes les autres réponses sont obsolètes).

En utilisant pandas> = 0.8, la réponse est:

In [30]: frame.resample('1H', how={'radiation': np.sum, 'tamb': np.mean})
Out[30]: 
                         tamb   radiation
2012-04-05 08:00:00  5.161235  279.507182
2012-04-05 09:00:00  4.968145  290.941073
2012-04-05 10:00:00  4.478531  317.678285
2012-04-05 11:00:00  4.706206  335.258633
2012-04-05 12:00:00  2.457873    8.655838
59
bmu

Pour vous titiller, dans pandas 0.8.0 (en développement lourd dans la branche timeseries sur GitHub), vous pourrez faire:

In [5]: frame.convert('1h', how='mean')
Out[5]: 
                     radiation      tamb
2012-04-05 08:00:00   7.840989  8.446109
2012-04-05 09:00:00   4.898935  5.459221
2012-04-05 10:00:00   5.227741  4.660849
2012-04-05 11:00:00   4.689270  5.321398
2012-04-05 12:00:00   4.956994  5.093980

Les méthodes mentionnées ci-dessus sont la bonne stratégie avec la version de production actuelle des pandas.

3
Wes McKinney

Vous pouvez également sous-échantillonner en utilisant la méthode asof de pandas.DateRange objets .

In [21]: hourly = pd.DateRange(datetime.datetime(2012, 4, 5, 8, 0),
...                          datetime.datetime(2012, 4, 5, 12, 0),
...                          offset=pd.datetools.Hour())

In [22]: frame.groupby(hourly.asof).size()
Out[22]: 
key_0
2012-04-05 08:00:00    60
2012-04-05 09:00:00    60
2012-04-05 10:00:00    60
2012-04-05 11:00:00    60
2012-04-05 12:00:00    1
In [23]: frame.groupby(hourly.asof).agg({'radiation': np.sum, 'tamb': np.mean})
Out[23]: 
                     radiation  tamb 
key_0                                
2012-04-05 08:00:00  271.54     4.491
2012-04-05 09:00:00  266.18     5.253
2012-04-05 10:00:00  292.35     4.959
2012-04-05 11:00:00  283.00     5.489
2012-04-05 12:00:00  0.5414     9.532
3
Garrett

Vous devez utiliser groupby comme tel:

grouped = frame.groupby(lambda x: x.hour)
grouped.agg({'radiation': np.sum, 'tamb': np.mean})
# Same as: grouped.agg({'radiation': 'sum', 'tamb': 'mean'})

la sortie étant:

        radiation      tamb
key_0                      
8      298.581107  4.883806
9      311.176148  4.983705
10     315.531527  5.343057
11     288.013876  6.022002
12       5.527616  8.507670

Donc, en gros, je divise la valeur de l'heure, puis je calcule la moyenne de tamb et la somme de radiation et je renvoie la DataFrame (approche similaire à celle de R ddply). Pour plus d'informations, je consulterais la page de documentation pour groupby ainsi que this blog.

Edit: Pour rendre cette échelle un peu meilleure, vous pouvez regrouper le jour et l'heure en tant que tels:

grouped = frame.groupby(lambda x: (x.day, x.hour))
grouped.agg({'radiation': 'sum', 'tamb': 'mean'})
          radiation      tamb
key_0                        
(5, 8)   298.581107  4.883806
(5, 9)   311.176148  4.983705
(5, 10)  315.531527  5.343057
(5, 11)  288.013876  6.022002
(5, 12)    5.527616  8.507670
1
diliop