Deedle - какой самый эффективный (самый быстрый) способ заменить элемент в столбце на основе значения другого элемента в другом столбце в той же строке - PullRequest
2 голосов
/ 05 марта 2020

У меня есть этот фрейм данных

     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_i равно not 0, то Mode_i и Setpoint_i будут значением выше (впереди) который AutoStat_i равен 0

Результат должен быть (обратите внимание, столбцы Setpoint_i и Mode_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

Что я пробовал: Мой Идея заключается в том, что для каждого набора i из (AutoStat_i, Mode_i, Setpoint_i) отсканируйте каждую строку, если AutoStat_i равен <> 0, затем установите другие значения в NaN, после этого я просто сделаю fillMissing с Direction.Forward. Ниже приведено описание имплантации

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 минут. Я вроде вижу, где это неправильно, поскольку для каждого набора он действует на весь набор данных, но я не могу придумать лучшего способа.

Логика c довольно проста. Я считаю, что должен быть лучший способ, сделайте совет, спасибо.

ОБНОВЛЕНИЕ Вот код для воспроизведения

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()

Советы / Ответ от Томаса

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

1 Ответ

3 голосов
/ 05 марта 2020

Прежде всего, я думаю, что ваша стратегия установки Mode_i и Setpoint_i в NA, когда AutoStat_i не 0, а затем заполнение пропущенных значений, является хорошим подходом.

Конечно, вы можете сделать это немного быстрее, переместив вызов 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
...