2014-02-16 12 views
6

Ich versuche, eine linke Verbindung zwischen zwei Datenrahmen in Deedle zu tun. Beispiele für die zwei Datenrahmen sind unter:Wie mache ich einen linken Join auf eine nicht eindeutige Spalte/Index in Deedle

let workOrders = 
    Frame.ofColumns [ 
     "workOrderCode" =?> series [ (20050,20050); (20051,20051); (20060,20060) ] 
     "workOrderDescription" =?> series [ (20050,"Door Repair"); (20051,"Lift Replacement"); (20060,"Window Cleaning") ]] 

// This does not compile due to the duplicate Work Order Codes 
let workOrderScores = 
    Frame.ofColumns [ 
     "workOrderCode" => series [ (20050,20050); (20050,20050); (20051,20051) ] 
     "runTime" => series [ (20050,20100112); (20050,20100130); (20051,20100215) ] 
     "score" => series [ (20050,100); (20050,120); (20051,80) ]] 

Frame.join JoinKind.Outer workOrders workOrderScores 

Das Problem ist, dass Deedle läßt mich nicht einen Datenrahmen mit einem nicht eindeutigen Index erstellen und ich erhalte die folgende Fehlermeldung: System.ArgumentException: Doppelter Schlüssel ‚20050‘ . Doppelte Schlüssel sind im Index nicht erlaubt.

Interessanterweise in Python/Pandas kann ich Folgendes tun, was perfekt funktioniert. Wie kann ich dieses Ergebnis in Deedle reproduzieren? Ich denke, dass ich den zweiten Datenrahmen abflachen muss, um die Duplikate zu entfernen, dann beitreten und dann wieder aufheben/entstapeln?

workOrders = pd.DataFrame(
    {'workOrderCode': [20050, 20051, 20060], 
     'workOrderDescription': ['Door Repair', 'Lift Replacement', 'Window Cleaning']}) 

workOrderScores = pd.DataFrame(
    {'workOrderCode': [20050, 20050, 20051], 
     'runTime': [20100112, 20100130, 20100215], 
     'score' : [100, 120, 80]}) 

pd.merge(workOrders, workOrderScores, on = 'workOrderCode', how = 'left') 

# Result: 
# workOrderCode workOrderDescription runTime score 
#0   20050   Door Repair 20100112 100 
#1   20050   Door Repair 20100130 120 
#2   20051  Lift Replacement 20100215  80 
#3   20060  Window Cleaning  NaN NaN 

Antwort

6

Dies ist eine große Frage - ich gebe zu müssen, gibt es derzeit keine elegante Möglichkeit, dies mit Deedle zu tun. Könnten Sie bitte ein Problem an GitHub senden, um sicherzustellen, dass wir dies verfolgen und eine Lösung hinzufügen?

Wie Sie sagen, nicht Deedle lassen Sie nicht in die Tasten doppelte Werte haben zur Zeit - obwohl Ihre Pandas Lösung nicht auch doppelte Schlüssel verwenden - Sie einfach die Tatsache nutzen, dass Pandas können Sie die Spalte angeben, zu verwenden, wenn Verbindung (und ich denke, das wäre eine großartige Ergänzung zu Deedle).

Hier ist eine Möglichkeit zu tun, was Sie wollten - aber nicht sehr nett. Ich denke, das Verwenden von Pivotieren wäre eine andere Option (es gibt eine nette Pivot-Tabellenfunktion im neuesten Quellcode - noch nicht in NuGet).

benutzte ich groupByRows und nest Ihre Datenrahmen in Serie durch die workOrderCode gruppiert zu drehen (jedes Element nun einen Rahmen mit allen Zeilen enthält, die die gleiche Arbeit um Code haben):

let workOrders = 
    Frame.ofColumns [ 
     "workOrderCode" =?> Series.ofValues [ 20050; 20051; 20060 ] 
     "workOrderDescription" =?> Series.ofValues [ "Door Repair"; "Lift Replacement"; "Window Cleaning" ]] 
    |> Frame.groupRowsByInt "workOrderCode" 
    |> Frame.nest 

let workOrderScores = 
    Frame.ofColumns [ 
     "workOrderCode" => Series.ofValues [ 20050; 20050; 20051 ] 
     "runTime" => Series.ofValues [ 20100112; 20100130; 20100215 ] 
     "score" => Series.ofValues [ 100; 120; 80 ]] 
    |> Frame.groupRowsByInt "workOrderCode" 
    |> Frame.nest 

Jetzt können wir beitreten die beiden Serien (weil ihre Arbeitsauftragscodes die Schlüssel sind). Sie jedoch ein oder zwei Daten dann erhalten Rahmen für jeden verbunden Code um und es ist ziemlich viel Arbeit erforderlich, um äußere die Reihen der beiden Rahmen zu verbinden:

// Join the two series to align frames with the same work order code 
Series.zip workOrders workOrderScores 
|> Series.map(fun _ (orders, scores) -> 
    match orders, scores with 
    | OptionalValue.Present s1, OptionalValue.Present s2 -> 
     // There is a frame with some rows with the specified code in both 
     // work orders and work order scores - we return a cross product of their rows 
     [ for r1 in s1.Rows.Values do 
      for r2 in s2.Rows.Values do 
      // Drop workOrderCode from one series (they are the same in both) 
      // and append the two rows & return that as the result 
      yield Series.append r1 (Series.filter (fun k _ -> k <> "workOrderCode") r2) ] 
     |> Frame.ofRowsOrdinal 
    // If left or right value is missing, we just return the columns 
    // that are available (others will be filled with NaN) 
    | OptionalValue.Present s, _ 
    | _, OptionalValue.Present s -> s) 
|> Frame.unnest 
|> Frame.indexRowsOrdinally 

Dies könnte langsam sein (vor allem im NuGet Ausführung). Wenn Sie an mehr Daten arbeiten, versuchen Sie bitte, die neueste Version von Deedle aus Quellen zu erstellen (und wenn das nicht hilft, bitte ein Problem einreichen - wir sollten uns das ansehen!)

+0

Danke für die Antwort. Es scheint nicht zu kompilieren, da ein Match-Case fehlt. Ich fügte den Fall hinzu "| _, _ -> frame [])" und das scheint es behoben zu haben, aber wie du gesagt hast, ist die Leistung nicht sehr gut. – jeremyh

+0

@jeremyh Das sollte nur eine Warnung sein (die Sie ignorieren können, weil Sie auf beiden Seiten niemals Werte verlieren werden). Aber Ihre Lösung ist eine großartige Möglichkeit, die Warnung zu vermeiden (und ich würde das gleiche im Produktionscode tun, um sicherzustellen, dass ich keine unerklärlichen Warnungen mehr habe!) –

+0

Oh ja, Sie sind richtig auf die Warnung. Wenn ich einen größeren Datensatz einspeise, kommt die Warnung sofort zurück, während die Ergebnisse verzögert werden. Ich dachte, es würde die Ergebnisse stoppen, aber es lief immer noch im Hintergrund. Ich werde dafür ein Problem auf GitHub hinzufügen. – jeremyh