2017-01-15 1 views
2

Ich habe zwei Dataframes indiziert durch das Datum, auf einem DataFrame ändern sich die Zeilen stündlich auf der anderen ändert es (manchmal ändert es sich alle 5 Minuten manchmal hat es einen anderen Schritt).Pandas addieren Werte zur richtigen Zeit

>>> print df2['value'] 
date 
2015-10-06 09:00:00 0.612303 
2015-10-06 10:00:00 0.482605 
2015-10-06 11:00:00 0.604132 

>>> print df1['value'] 
date 
2015-10-06 09:05:00 0.412303 
2015-10-06 09:08:00 0.112303 
2015-10-06 09:28:00 0.
2015-10-06 10:15:00 0.000005 
2015-10-06 11:00:00 0.133132 

Ich möchte die df1 Werte, die zwischen Stunde h und h + 1 haben, um die entsprechenden df2 Wert sind.

Gewünschtes Ergebnis:

>>> print df1['value'] 
date 
2015-10-06 09:05:00 0.612303 
2015-10-06 09:08:00 0.612303 
2015-10-06 09:28:00 0.612303 
2015-10-06 10:15:00 0.482605 
2015-10-06 11:00:00 0.604132 

Wie kann ich das erreichen?

+1

['merge_asof'] (http://pandas.pydata.org/pandas-docs/stable/merging.html#merging-asof) existiert für diesen Zweck. –

+0

@ajcr das mein Problem gelöst hat. Danke, willst du mir eine Antwort schreiben? –

+0

Ich habe eine Antwort mit 'merge_asof' unten hinzugefügt - lassen Sie mich wissen, wenn Sie weitere Details wünschen. –

Antwort

4

Sie merge_asof verwenden können, um eine korrekte Spalte von Werten für Ihr Beispiel mit einem DatetimeIndex zu generieren:

pd.merge_asof(df1.reset_index(), # see note about reset_index below 
       df2.reset_index(), 
       on='date', 
       tolerance=pd.Timedelta('1H')) 

Der Toleranzparameter ermöglicht es Ihnen, wie lange nach einer bestimmten Zeit fest, bis Die Zusammenführung sollte gültig sein. Wenn Sie beispielsweise (10 Minuten) angeben, können nicht alle Werte zusammengeführt werden und einige Standorte werden stattdessen mit NaN markiert.


Beachten Sie, dass ich den Index der beiden Frames vor dem Zusammenführen zurücksetzen musste. Ich wollte mit left_index=True und right_index=True verschmelzen, aber Pandas weigert sich, dies zuzulassen, wenn auch ein Toleranzparameter übergeben wird (dies ist möglicherweise ein Fehler - mit merge_asof sollte hier noch besser sein, wenn geändert).

+1

FWIW Ich habe ein Problem auf [GitHub] (https://github.com/pandas-dev/pandas/issues/15135) –

+2

... und ein [Bugfix] (https://github.com/) geöffnet pandas-dev/pandas/pull/15139) wurde zu master zusammengeführt. Sollte in der nächsten Pandas-Version behoben werden. –

0

In Ihrem df1 erstellen Sie eine neue Spalte "stündlich" von ihrem Index Dann einfach df1 mit df2 auf dieser neu erstellten Spalte verbinden, um die Werte von df2 hinzuzufügen.

Wird Code schreiben so schnell wie möglich :)

EDIT: wie versprochen, hier ist der Code

import pandas as pd 
from datetime import datetime 

df1 = pd.read_csv("df1.csv",index_col="date", parse_dates=True) 
df2 = pd.read_csv("df2.csv",index_col="date", parse_dates=True) 


def fromTimeStampToHour(date): 
    datetimeObj = date.to_datetime() 
    hourlyObj = datetime(year=datetimeObj.year,month= datetimeObj.month, day = datetimeObj.day, hour=datetimeObj.hour) 
    return hourlyObj 

df1["Hours"] = df1.index.map(lambda x: fromTimeStampToHour(x)) 

print pd.merge(left=df1, right=df2, left_on="Hours", right_index=True, suffixes=("_df1", "_df2")) 
1

Für Pandas Version 0.19.0 oder besser finden ajcr's answer.


Für Pandas Versionen < 0.19.0: Sie könnten die beiden Datenrahmen kombinieren, mit concat, ffill verwenden die NaN-Werte mit den Soll-Werten zu zukunfts füllen, und dann updatedf1 mit diesen Werten:

import pandas as pd 
df2 = pd.DataFrame({'value':[0.612303,0.482605,0.604132]}, index=pd.DatetimeIndex(['2015-10-06 09:00:00', '2015-10-06 10:00:00', '2015-10-06 11:00:00'])) 
df1 = pd.DataFrame({'value':[0.412303, 0.112303, 0., 0.000005, 0.133132]}, index=pd.DatetimeIndex(['2015-10-06 09:05:00', '2015-10-06 09:08:00', '2015-10-06 09:28:00', '2015-10-06 10:15:00', '2015-10-06 11:00:00'])) 

df1.update(pd.concat([df1, df2], axis=1).ffill().iloc[:, 1]) 
print(df1) 

ergibt

     value 
2015-10-06 09:05:00 0.612303 
2015-10-06 09:08:00 0.612303 
2015-10-06 09:28:00 0.612303 
2015-10-06 10:15:00 0.482605 
2015-10-06 11:00:00 0.604132 

Alternativ können Sie searchsorted verwenden, um die Indexwerte zu finden, die angeben, wo df1.index passt in df2.index:

import pandas as pd 
df2 = pd.DataFrame({'value':[0.612303,0.482605,0.604132]}, index=pd.DatetimeIndex(['2015-10-06 09:00:00', '2015-10-06 10:00:00', '2015-10-06 11:00:00'])) 
df1 = pd.DataFrame({'value':[0.412303, 0.112303, 0., 0.000005, 0.133132]}, index=pd.DatetimeIndex(['2015-10-06 09:05:00', '2015-10-06 09:08:00', '2015-10-06 09:28:00', '2015-10-06 10:15:00', '2015-10-06 11:00:00'])) 

df1['value'] = df2.iloc[df2.index.searchsorted(df1.index, side='right')-1].values 
print(df1) 

ergibt

     value 
2015-10-06 09:05:00 0.612303 
2015-10-06 09:08:00 0.612303 
2015-10-06 09:28:00 0.612303 
2015-10-06 10:15:00 0.482605 
2015-10-06 11:00:00 0.604132 

Beachten Sie, dass searchsorted geht davon aus, dass df2.index ist bereits in sortierter Reihenfolge. Wenn dies nicht der Fall ist, verwenden Sie zuerst df2 = df2.sort_index().

Im Gegensatz dazu pd.concat gibt einen Datenrahmen, dessen DatatimeIndex ist in sortiert Ordnung, auch wenn df1.index und/oder df2.index ist nicht sortiert. So ist für die erste Methode der Aufruf sort_index nicht notwendig.


Von diesen beiden Methoden ist searchsorted schneller. Zum Beispiel mit diesem Setup:

import numpy as np 
import pandas as pd 
N = 1000 
df1 = pd.DataFrame(np.random.random(N), index=pd.date_range('2000-1-1', periods=N, freq='14T')) 
df2 = pd.DataFrame(np.random.random(int(N/60*14)), index=pd.date_range('2000-1-1', periods=int(N/60*14), freq='1H')) 
df3, df4 = df1.copy(), df1.copy() 

df3.update(pd.concat([df3, df2], axis=1).ffill().iloc[:, 1]) 
df4[0] = df2.iloc[df2.index.searchsorted(df4.index, side='right')-1].values 
assert df3.equals(df4) 

searchsorted ist ~ 2.8x schneller:

In [88]: %timeit df3.update(pd.concat([df3, df2], axis=1).ffill().iloc[:, 1]) 
100 loops, best of 3: 2.13 ms per loop 

In [89]: %timeit df4[0] = df2.iloc[df2.index.searchsorted(df4.index, side='right')-1].values 
1000 loops, best of 3: 744 µs per loop 

In [90]: len(df1), len(df2) 
Out[90]: (1000, 233) 
Verwandte Themen