Deedle - 根据同一行另一列中另一个项目的值替换列中项目的最有效(最快)方法是什么

Deedle - what is most efficient (fastest) way to replace an item in a column based on value of another item in another column on the same row

我有这个数据框

     AutoStat_1 AutoStat_2 Mode_1 Mode_2 Setpoint_1 Setpoint_2 
0 -> 0          0          1      1      23         24
1 -> 0          1          1      0      23         27
2 -> 1          1          3      0      26         27         
3 -> 1          0          3      1      26         24
4 -> 0          0          1      2      24         24
5 -> 0          0          1      2      24         24
6 -> 2          3          0      4      24         26
7 -> 2          3          0      4      25         26

要求是,如果AutoStat_inot 0,那么Mode_iSetpoint_i将是上面(前面)的值,其中AutoStat_i0

结果应该是(注意 Setpoint_iMode_i 列与上面的不同)

     AutoStat_1 AutoStat_2 Mode_1 Mode_2 Setpoint_1 Setpoint_2
0 -> 0          0          1      1      23         24
1 -> 0          1          1      1      23         24
2 -> 1          1          1      1      23         24
3 -> 1          0          1      1      23         24
4 -> 0          0          1      2      24         24
5 -> 0          0          1      2      24         24
6 -> 2          3          1      2      24         24
7 -> 2          3          1      2      24         24

我尝试了什么: 我的想法是对于 (AutoStat_i, Mode_i, Setpoint_i) 的每个集合 i,扫描每一行,如果 AutoStat_i<> 0 然后将其他值设置为 NaN,之后我将用 Direction.ForwardfillMissing。下面是实现

let calculateNonSFi (df:Frame<_,string>) idx = 
    let autoStatusName = sprintf "AutoStat_%d" idx
    let setpointName   = sprintf "Setpoint_%d" idx
    let modeName       = sprintf "Mode_%d" idx
    let setMissingOnMode (s:ObjectSeries<string>) =
        let s2 = s.As<float>()
        if s2.[autoStatusName] <> 0. then
            Series.replaceArray [|setpointName;modeName|] Double.NaN s2
        else
            s2
    df.Rows
    |> Series.mapValues setMissingOnMode
    |> Frame.ofRows
    |> Frame.fillMissing Direction.Forward
    |> Frame.fillMissing Direction.Backward

// for each set i do the folding
[0..150]
|> List.fold calculateNonSFi df

它给了我预期的结果,但是,对于 150 组 8000 行,需要 30 多分钟才能完成。我有点明白它对整个数据集起作用的每个集合哪里错了,但我想不出更好的方法。

逻辑很简单。相信应该有更好的方法,多多指教,谢谢

更新 这是复制代码

open Deedle
open System
let df = 
    [
        {| AutoStat_1=0;Setpoint_1=23;Mode_1=1;AutoStat_2=0;Setpoint_2=24;Mode_2=1|}
        {| AutoStat_1=0;Setpoint_1=23;Mode_1=1;AutoStat_2=1;Setpoint_2=24;Mode_2=1|}
        {| AutoStat_1=1;Setpoint_1=23;Mode_1=1;AutoStat_2=1;Setpoint_2=24;Mode_2=1|}
        {| AutoStat_1=1;Setpoint_1=23;Mode_1=1;AutoStat_2=0;Setpoint_2=24;Mode_2=1|}
        {| AutoStat_1=0;Setpoint_1=24;Mode_1=1;AutoStat_2=0;Setpoint_2=24;Mode_2=2|}
        {| AutoStat_1=0;Setpoint_1=24;Mode_1=1;AutoStat_2=0;Setpoint_2=24;Mode_2=2|}
        {| AutoStat_1=2;Setpoint_1=24;Mode_1=1;AutoStat_2=3;Setpoint_2=24;Mode_2=2|}
        {| AutoStat_1=2;Setpoint_1=24;Mode_1=1;AutoStat_2=3;Setpoint_2=24;Mode_2=2|}
    ] |> Frame.ofRecords
df.Print()

let calculateNonSFi (df:Frame<_,string>) idx = 
    let autoStatusName = sprintf "AutoStat_%d" idx
    let setpointName   = sprintf "Setpoint_%d" idx
    let modeName       = sprintf "Mode_%d" idx
    let setMissingOnMode (s:ObjectSeries<string>) =
        let s2 = s.As<float>()
        if s2.[autoStatusName] <> 0. then
            Series.replaceArray [|setpointName;modeName|] Double.NaN s2
        else
            s2
    df.Rows
    |> Series.mapValues setMissingOnMode
    |> Frame.ofRows
    |> Frame.fillMissing Direction.Forward

let df1 = 
    [1..2]
    |> List.fold calculateNonSFi df
df1.Print()

Advice/Answer 来自托马斯

df
|> Frame.mapRows (fun _ o -> 
  [ for i in 0 .. 150 do
      let au = o.GetAs<float>("AutoStat_" + string i)
      yield "AutoStat_" + string i, au
      yield "Mode_" + string i, if au <> 0. then nan else o.GetAs("Mode_" + string i)
      yield "Setpoint_" + string i, if au <> 0. then nan else o.GetAs("Setpoint_" + string i) ]
  |> series )
|> Frame.ofRows
|> Frame.fillMissing Direction.Forward

它产生了正确的结果,但列顺序不同,因此我在之前的编辑中犯了错误

     AutoStat_1 Mode_1 Setpoint_1 AutoStat_2 Mode_2 Setpoint_2 
0 -> 0          1      23         0          1      24
1 -> 0          1      23         1          1      24
2 -> 1          1      23         1          1      24         
3 -> 1          1      23         0          1      24
4 -> 0          1      24         0          2      24
5 -> 0          1      24         0          2      24
6 -> 2          1      24         3          2      24
7 -> 2          1      24         3          2      24

首先,我认为你的策略是当 AutoStat_i 不是 0 时将 Mode_iSetpoint_i 设置为 NA 然后填充缺失的values 是一个很好的方法。

您当然可以通过将 fillMissing 调用移动到 calculateNonSFi 函数之外来使其更快一些 - fillMissing 操作将在整个帧上 运行,所以你需要在最后运行一次。

第二件事是找到一种方法来设置 NA 值,该值只在帧上迭代一次。一种选择(我没有测试过)是使用 Frame.mapRows 并在函数内部遍历所有列(而不是遍历所有列并重复调用 mapRows )。类似于:

df
|> Frame.mapRows (fun _ o -> 
  [ for i in 0 .. 150 do
      let au = o.GetAs<float>("AutoStat_" + string i)
      yield "AutoStat_" + string i, au
      yield "Mode_" + string i, if au = 0. then nan else o.GetAs("Mode_" + string i)
      yield "Setpoint_" + string i, if au = 0. then nan else o.GetAs("Setpoint_" + string i) ]
  |> series )
|> Frame.ofRows