2017-05-05 63 views
1

我期待以与pandas.DataFrame.Merge类似的方式基于每个帧中的特定列合并两个Deedle(F#)帧。其中最好的例子是包含数据列的主帧和(城市,州)列以及包含以下列的信息框:(城市,州);土地增值税;长。如果我想将纬度长列添加到我的主框架中,我会合并(城市,州)列上的两个框架。与pandas.merge相当的指针

下面是一个例子:

let primaryFrame = 
      [(0, "Job Name", box "Job 1") 
      (0, "City, State", box "Reno, NV") 
      (1, "Job Name", box "Job 2") 
      (1, "City, State", box "Portland, OR") 
      (2, "Job Name", box "Job 3") 
      (2, "City, State", box "Portland, OR") 
      (3, "Job Name", box "Job 4") 
      (3, "City, State", box "Sacramento, CA")] |> Frame.ofValues 

    let infoFrame = 
      [(0, "City, State", box "Reno, NV") 
      (0, "Lat", box "Reno_NV_Lat") 
      (0, "Long", box "Reno_NV_Long") 
      (1, "City, State", box "Portland, OR") 
      (1, "Lat", box "Portland_OR_Lat") 
      (1, "Long", box "Portland_OR_Long")] |> Frame.ofValues 

    // see code for merge_on below. 
    let mergedFrame = primaryFrame 
         |> merge_On infoFrame "City, State" null 

这将导致“mergedFrame”看起来像这样:

> mergedFrame.Format();; 
val it : string = 
    "  Job Name City, State Lat    Long    
0 -> Job 1 Reno, NV  Reno_NV_Lat  Reno_NV_Long  
1 -> Job 2 Portland, OR Portland_OR_Lat Portland_OR_Long 
2 -> Job 3 Portland, OR Portland_OR_Lat Portland_OR_Long 
3 -> Job 4 Sacramento, CA <missing>  <missing> 

我想出了这样做(以下简称“merge_on”功能的一种方式在上面的例子中使用过),但是作为F#新手的销售工程师,我想有一种更习惯性/有效的方式来做到这一点。下面是我的函数,以及'removeDuplicateRows',它可以满足您期望的和'merge_on'函数的需要;如果您想对更好的方式发表评论,请做。

let removeDuplicateRows column (frame : Frame<'a, 'b>) = 
      let nonDupKeys = frame.GroupRowsBy(column).RowKeys 
           |> Seq.distinctBy (fun (a, b) -> a) 
           |> Seq.map (fun (a, b) -> b) 
      frame.Rows.[nonDupKeys] 


    let merge_On (infoFrame : Frame<'c, 'b>) mergeOnCol missingReplacement 
        (primaryFrame : Frame<'a,'b>) = 
      let frame = primaryFrame.Clone() 
      let infoFrame = infoFrame       
          |> removeDuplicateRows mergeOnCol 
          |> Frame.indexRows mergeOnCol 
      let initialSeries = frame.GetColumn(mergeOnCol) 
      let infoFrameRows = infoFrame.RowKeys 
      for colKey in infoFrame.ColumnKeys do 
       let newSeries = 
        [for v in initialSeries.ValuesAll do 
         if Seq.contains v infoFrameRows then 
          let key = infoFrame.GetRow(v) 
          yield key.[colKey] 
         else 
          yield box missingReplacement ] 
       frame.AddColumn(colKey, newSeries) 
      frame 

感谢您的帮助!

UPDATE:

交换Frame.indexRowsString到Frame.indexRows以应对在 'mergOnCol' 的类型不是字符串的情况。

摆脱infoFrame.Clone()的由Tomas

回答

0

方式的建议Deedle不(仅行/列键)帧的加盟黯然意味着它不会有一个很好的内置函数做通过非关键列连接帧。

据我所见,你的方法对我来说很好。你不需要infoFrame上的Clone(因为你没有改变帧),我认为你可以用infoFrame.TryGetRow代替infoFrame.GetRow(然后你不需要事先得到密钥),但除此之外,你的代码看起来像精细!

我想出了一个替代和这样做的有点短的方式,它看起来如下:

// Index the info frame by city/state, so that we can do lookup 
let infoByCity = infoFrame |> Frame.indexRowsString "City, State" 

// Create a new frame with the same row indices as 'primaryFrame' 
// containing the additional information from infoFrame. 
let infoMatched = 
    primaryFrame.Rows 
    |> Series.map (fun k row -> 
     // For every row, we get the "City, State" value of the row and then 
     // find the corresponding row with additional information in infoFrame. Using 
     // 'ValueOrDefault' will automatically give missing when the key does not exist 
     infoByCity.Rows.TryGet(row.GetAs<string>("City, State")).ValueOrDefault) 
    // Now turn the series of rows into a frame 
    |> Frame.ofRows 

// Now we have two frames with matching keys, so we can join! 
primaryFrame.Join(infoMatched) 

这是一个有点短,也许更不言自明,但我没有做任何测试检查哪一个更快。除非性能是主要问题,否则我认为使用更易读的版本是一个很好的默认选择!

+0

感谢您的帮助。我在我的实际项目上运行了这个,并有一些评论。这工作除了我仍然需要添加一个“removeDuplicateRows”的情况下(坚持这个例子)的信息框架可能有两次列出相同的“城市,国家”。此外,我无法让我的一个infoFrames与这个函数合并,但它会与上面的一个合并。另外, –

+0

最后我跑了一些测试。在(行,列)中,主帧=(29635,7),第一信息帧=(2572,4),第二信息帧=(18601,2)。 5次合并所有三个平均5.97秒。用我的方法和27.05秒。与你的。尽管如此,感谢您对此进行调查并迅速回答! –

+0

不错的工作,并感谢您运行测试:-)听起来像你的方式是要走的路! –