2017-12-06 3 views
2

Ich versuche, 2 Datenrahmen basierend auf den 2 Spalten zu verbinden. Das Problem ist, dass ich einen der Spaltenwerte interpolieren möchte.Verbinden Sie Pandas basierend auf Spalteninterpolation

df1 = pd.DataFrame(data=[['ABC', 'USD', 2.31], ['DEF', 'MXN', 4.72], ['XYZ', 'EUR', 5.83]], columns=['A', 'B', 'C']) 
>>df1 
    A B  C 
0 ABC USD 2.31 
1 DEF MXN 4.72 
2 XYZ EUR 5.83 

df2 = pd.DataFrame(data=[['USD', 1, 0.5], ['USD', 2, 0.8], ['USD', 3, 1.5], ['MXN', 2, 0.6], ['MXN', 3, 0.71], ['MXN', 4, 0.88], ['EUR', 6, 0.12], ['EUR', 7, 0.5], ['EUR', 8, 0.7]], columns=['B', 'C', 'V']) 
>>df2 
    B C  V 
0 USD 1 0.50 
1 USD 2 0.80 
2 USD 3 1.50 
3 MXN 2 0.60 
4 MXN 3 0.71 
5 MXN 4 0.88 
6 EUR 6 0.12 
7 EUR 7 0.50 
8 EUR 8 0.70 

Ich möchte die zwei Datenrahmen basierend auf Spalten B und C verbinden, so dass ich das folgende Ergebnis

 A B  C  V 
0 ABC USD 2.31 1.017 
1 DEF MXN 4.72 0.880 
2 XYZ EUR 5.83 0.120 

Die Interpolation ist linear und verwendet die nächsten Punkte, wenn außer Reichweite .

Der erste Wert

1.017=0.8+(2.31-2)*(1.5-0.8) 

Der zweite Wert ist es, da der Maximalwert MXN in DF2 hat 0,6

ähnlich Der dritte Wert ist der für EUR Maximalwert ist, die 0,12

ist

Das Ziel ist, dies so effizient wie möglich zu tun, da die Datenrahmen ziemlich groß sind.

Ab sofort gruppiere ich nach B und benutze die scipy interpolate Funktion.

+0

Dies ist mein aktueller Ansatz - 'ccy_dict = {ccy: interp1d (df [ 'C'], df [ 'V'], bounds_error = False, fill_value = (df [ 'V']. Iloc [0], df ['V']. Iloc [-1])) für ccy, df in ccy_df.groupby ('B')} ' Gefolgt von - ' df1 ['V'] = df1. anwenden (Lambda x: ccy_dict [x ['B']] (x ['C']), Achse = 1) ' –

Antwort

1

Hier ist die Lösung, die ich gefunden habe, obwohl ich denke, dass Sie dies wahrscheinlich verbessern könnten, indem Sie eine Anwendung mit scipy.interpolate für den Interpolationsschritt verwenden. Erstellen Sie zuerst Boden-, Decken- und Deltasäulen für df1.

enter df1['C_floor'] = df1.C.apply(np.floor) 
df1['C_ceil'] = df1.C.apply(np.ceil) 
df1['C_delta'] = df1.C - df1.C_floor 

ein Doppel links trete die V entsprechend dem Boden und Decke von C von df2 zu erhalten.

df1 = df1.merge(df2, how ='left', left_on = ['B', 'C_floor'], right_on = ['B', 'C']) 
df1 = df1.merge(df2, how ='left', left_on = ['B', 'C_ceil'], right_on = ['B', 'C']) 

Hier ist, wo ich denke, man könnte der Code schneller machen, aber ich schrieb eine kurze Funktion des gewichteten Mittels und Konto für die Fälle zu finden, wo C außerhalb des Bereichs in df2 verfügbar fällt.

def weighted_mean(x): 
    if np.isnan(x.V_x): 
     return x.V_y 
    elif np.isnan(x.V_y): 
     return x.V_x 
    else: 
     return x.V_y + (x.V_x - x.V_y) * x.C_delta 

df1['V'] = df1[['V_x', 'V_y', 'C_delta']].apply(weighted_mean, axis = 1) 

Endlich einige Aufräumarbeiten.

df1 = df1[['A', 'B', 'C_x', 'V']] 
df1.columns = ['A', 'B', 'C', 'V'] 

Welche gibt.

 A B  C  V 
0 ABC USD 2.31 1.283 
1 DEF MXN 4.72 0.880 
2 XYZ EUR 5.83 0.120 
+0

Danke, das ist eine großartige Lösung! Funktioniert für mich zu dieser Zeit. Das einzige Problem ist, dass es nicht funktioniert, wenn die Spalte ** C ** ebenfalls ein Float ist. –

+0

Wenn es sich um einen gleichmäßig verteilten Puffer mit einem regelmäßigen Intervall handelt. 10.1, 10.2 usw. können Sie einfach die Spalte "C" mit 10 (oder einer beliebigen Zahl) multiplizieren, als "int" darstellen und den exakt gleichen Code verwenden, solange Sie die Funktionen "ceil" und "floor" ändern zu dem geeigneten Intervall. Was wäre herausfordernd und ein anderes Problem insgesamt, wenn die Intervalle nicht gleichmäßig verteilt sind. – doktakay

Verwandte Themen