2015-10-29 19 views
5

Voglio unire due frame di dati basati su due colonne: "Codice" e "Data". È semplice unire i frame di dati basati su "Code", tuttavia in caso di "Date" diventa complicato - non esiste una corrispondenza esatta tra Date in df1 e df2. Quindi, voglio selezionare le date più vicine. Come posso fare questo?Come unire due frame di dati in base alla data più vicina

df = df1[column_names1].merge(df2[column_names2], on='Code') 
+0

domanda relativa: http://stackoverflow.com/questions/24614474/pandas-merge-on-name-and-closest-date – jakevdp

+0

Ecco una risposta migliore: http://stackoverflow.com/questions/21201618/pandas-merge-match-the-nearest-time-stamp-the-series-of-timestamps – jakevdp

+0

@jakevdp: Grazie, ma come posso combinarlo con l'unione per codice? Dovrei prima usare "searchsorted" e quindi inserire "mask = idx> = 0 & ..."? –

risposta

6

Io non credo che ci sia un rapido, una riga modo per fare questo genere di cose, ma I belive l'approccio migliore è quello di fare in questo modo:

  1. aggiungere una colonna df1 con la data più vicina dal gruppo appropriato in df2

  2. chiamata una fusione di serie su questi

Con l'aumentare delle dimensioni dei dati, questa operazione "data più vicina" può diventare piuttosto costosa a meno che non si faccia qualcosa di sofisticato. Mi piace usare il codice NearestNeighbor di scikit-learn per questo genere di cose.

Ho messo insieme un approccio a quella soluzione che dovrebbe scalare relativamente bene. Prima siamo in grado di generare alcuni dati semplici:

import pandas as pd 
import numpy as np 
dates = pd.date_range('2015', periods=200, freq='D') 

rand = np.random.RandomState(42) 
i1 = np.sort(rand.permutation(np.arange(len(dates)))[:5]) 
i2 = np.sort(rand.permutation(np.arange(len(dates)))[:5]) 

df1 = pd.DataFrame({'Code': rand.randint(0, 2, 5), 
        'Date': dates[i1], 
        'val1':rand.rand(5)}) 
df2 = pd.DataFrame({'Code': rand.randint(0, 2, 5), 
        'Date': dates[i2], 
        'val2':rand.rand(5)}) 

Diamo controllare questi fuori:

>>> df1 
    Code  Date  val1 
0  0 2015-01-16 0.975852 
1  0 2015-01-31 0.516300 
2  1 2015-04-06 0.322956 
3  1 2015-05-09 0.795186 
4  1 2015-06-08 0.270832 

>>> df2 
    Code  Date  val2 
0  1 2015-02-03 0.184334 
1  1 2015-04-13 0.080873 
2  0 2015-05-02 0.428314 
3  1 2015-06-26 0.688500 
4  0 2015-06-30 0.058194 

Ora scriviamo una funzione apply che aggiunge una colonna di date più vicine a df1 utilizzando scikit-learn:

from sklearn.neighbors import NearestNeighbors 

def find_nearest(group, match, groupname): 
    match = match[match[groupname] == group.name] 
    nbrs = NearestNeighbors(1).fit(match['Date'].values[:, None]) 
    dist, ind = nbrs.kneighbors(group['Date'].values[:, None]) 

    group['Date1'] = group['Date'] 
    group['Date'] = match['Date'].values[ind.ravel()] 
    return group 

df1_mod = df1.groupby('Code').apply(find_nearest, df2, 'Code') 
>>> df1_mod 
    Code  Date  val1  Date1 
0  0 2015-05-02 0.975852 2015-01-16 
1  0 2015-05-02 0.516300 2015-01-31 
2  1 2015-04-13 0.322956 2015-04-06 
3  1 2015-04-13 0.795186 2015-05-09 
4  1 2015-06-26 0.270832 2015-06-08 

Infine, possiamo unire questi insieme con una semplice chiamata a pd.merge:

>>> pd.merge(df1_mod, df2, on=['Code', 'Date']) 
    Code  Date  val1  Date1  val2 
0  0 2015-05-02 0.975852 2015-01-16 0.428314 
1  0 2015-05-02 0.516300 2015-01-31 0.428314 
2  1 2015-04-13 0.322956 2015-04-06 0.080873 
3  1 2015-04-13 0.795186 2015-05-09 0.080873 
4  1 2015-06-26 0.270832 2015-06-08 0.688500 

Si noti che le righe 0 e 1 erano entrambe uguali allo val2; questo è previsto dato il modo in cui hai descritto la soluzione desiderata.

+0

Non correlato a questa domanda ma il tuo SKYARN PYCON 2015 è stato molto bello. Molto apprezzato per la condivisione! – WoodChopper

+0

Ho un problema con le date nel formato: 2015-10-19T07: 42: 00.000 Hai qualche idea su come analizzarle in modo che il codice funzioni? –

+0

È possibile convertire da stringa a data con '' pd.to_datetime() '' – jakevdp

0

Ecco una soluzione alternativa:

  1. Unisci il codice.

  2. Aggiungere una colonna di differenza di data in base alle proprie esigenze (ho utilizzato abs nell'esempio seguente) e ordinare i dati utilizzando la nuova colonna.

  3. Raggruppa in base ai record del primo frame di dati e per ciascun gruppo prende un record dal secondo frame di dati con la data più vicina.

Codice:

df = df1.reset_index()[column_names1].merge(df2[column_names2], on='Code') 
df['DateDiff'] = (df['Date1'] - df['Date2']).abs() 
df.sort_values('DateDiff').groupby('index').first().reset_index() 
Problemi correlati