我如何在F#中优雅地操作这个Deedle.Frame

时间:2017-01-18 11:44:27

标签: visual-studio-2015 f# deedle

我一直在与F#斗争,特别是与Deedle.Frame和Deedle.Series合作。

  1. 我需要创建两个新列,其值依赖于其他列。
  2. 对于某些行,我需要插入一个新行。
  3. 我当前的解决方案非常难看,基本上根据每个if语句过滤Frame,并返回一个仅包含新列的新Frame。然后我把它合并到原始的Frame中。这种情况起作用或相互排斥。我需要插入一个新行的情况是这样的,我将重新索引新帧,以便在合并期间它只是附加在原始帧的底部(在最后一行之后)。

    但是,由于不同的if-cases /模式很多,我必须处理大量的框架合并,并确保处理IF语句的补充(否则会丢失值)。 / p>

    Input Frame -> Target Frame (row order not important)

    // ****** PSEUDO CODE ******
    // Create new columns as copies.
    Add column ["NEWTYPE"] = ["TYPE"] 
    Add column ["NEWVALUE"] = ["VALUE"]
    // Iterate over rows.
    foreach row in table
        IF row["GROUP"] == A
            typeParts = row["TYPE"].Split('/')
            // Swap order X/Y -> Y/X.
            IF typeParts[0] == "X" && typeParts[1] != "X"
              row["NEWTYPE"] = typeParts[1] + "/" + "X"
              row["NEWVALUE"] = row["VALUE"] / 10.0
            // Split row into two rows Z/Y -> {Z/X, Y/X}.
            ElseIF typeParts[0] != "X" && typeParts[1] != "X"
                Insert extraRow = row
                extraRow["NEWTYPE"] = typeParts[0] + "/" + "X"
                extraRow.["NEWVALUE"] = row["VALUE"] * 2.0
                row["NEWTYPE"] = typeParts[1] + "/" + "X"
                row["NEWVALUE"] = row["VALUE"] * 3.0
            ELSE
                // Do nothing, new columns already copied.
        ELSE
              // Do nothing, new columns already copied.
    

    任何能够提出良好解决方案的人的金星。我想这可以通过返回一个框架列表来解决(因为可能会创建一个新行)然后展平并合并所有?

    ***** Here is my current ugly F# code: *****
    
    let subA =
        inputFrame
        |> Frame.filterRowValues(fun row -> row.GetAs<string>("GROUP") = "A")
    
    let grpSwap =
        subA 
        |> Frame.filterRowValues(fun row -> 
            let typeParts = row.GetAs<string>("TYPE").Split('/')
            typeParts.[0] = "X" && typeParts.[1] <> "X")
        |> Frame.mapRowValues(fun r -> 
            let typeParts = r.GetAs<string>("TYPE").Split('/')
            series ["NEWTYPE" => box (typeParts.[1] + "/" + typeParts.[0]); "NEWVALUE" => box (r.GetAs<float>("VALUE") / 10.0)])
        |> Frame.ofRows
    
    let grpCopy =
        subA 
        |> Frame.filterRowValues(fun row -> 
            let typeParts = row.GetAs<string>("TYPE").Split('/')
            typeParts.[0] <> "X" && typeParts.[1] = "X")
        |> Frame.mapRowValues(fun r -> 
            series ["NEWTYPE" => box (r.GetAs<string>("TYPE")); "NEWVALUE" => box (r.GetAs<float>("VALUE"))])
        |> Frame.ofRows
    
    
    let rowsToSplit =
        subA 
        |> Frame.filterRowValues(fun row -> 
            let typeParts = row.GetAs<string>("TYPE").Split('/')
            typeParts.[0] <> "X" && typeParts.[1] <> "X")
    
    let grpSplit1 =
        rowsToSplit 
        |> Frame.mapRowValues(fun r -> 
            let typeParts = r.GetAs<string>("TYPE").Split('/')
            series ["NEWTYPE" => box (typeParts.[0] + "/" + "X"); "NEWVALUE" => box (r.GetAs<float>("VALUE") * 2.0)])
        |> Frame.ofRows
    
    let grpSplit2 =
        rowsToSplit
        |> Frame.mapRowValues(fun r -> 
            let typeParts = r.GetAs<string>("TYPE").Split('/')
            series ["NEWTYPE" => box (typeParts.[1] + "/" + "X"); "NEWVALUE" => box (r.GetAs<float>("VALUE") * 3.0)])
        |> Frame.ofRows
    
    let grpAComplement =
        inputFrame 
        |> Frame.filterRowValues(fun row -> 
            row.GetAs<string>("GROUP") <> "A")
        |> Frame.mapRowValues(fun r -> 
            series ["NEWTYPE" => box (r.GetAs<string>("TYPE")); "NEWVALUE" => box (r.GetAs<float>("VALUE"))])
        |> Frame.ofRows
    
    
    let outputFrame = 
        let final0 = Frame.mergeAll([inputFrame; grpSwap; grpCopy; grpSplit1; grpAComplement])
        let appendFromIndex = (final0.RowCount)
        let appendToIndex = appendFromIndex + (grpSplit2.RowCount-1)
        let newRow = grpSplit2 |> Frame.merge rowsToSplit
        newRow |> Frame.indexRowsWith [appendFromIndex..appendToIndex] |> Frame.merge final0
    

1 个答案:

答案 0 :(得分:4)

我会尝试这样的事情

首先定义一些类型以使事情更容易处理

type Group = 
    | A
    | B
    | C

type Source = {
    Group: Group
    Typ: string
    Value: float
}          

type Target = {
    Group: Group
    Typ: string
    Value: float
    NType: string
    NValue: float
}          

创建初始化您的初始列表

let xs : List<Source> = createFromWhereEver()

定义转换函数 诀窍是此函数返回Target个对象的列表。可以是一个项目,也可以是两个项目。

let transform (x:Source) : List<Target> =
    if x.Group = A then
        let init x ntype nvalue =
            {
                Group = x.Group
                Typ = x.Typ
                Value = x.Value
                NType = ntype
                NValue = nvalue
            }
        let tp0 :: tp1 :: _ = x.Typ.split('/')
        // Swap order X/Y -> Y/X.
        if tp0 = "X" && tp1 <> "X" then
            [init x (tp1 + "/X") (x.value / 10)]
        // Split row into two rows Z/Y -> {Z/X, Y/X}.
        elif tp0 <> "X" && tp1 <> "X"
            [
                init x (tp0 + "/X") (x.value * 2)
                init x (tp1 + "/X") (x.value * 3)
            ]
        else
            [x]
    else
        [x]

最后通过地图抽取你的清单和资源,最后连续这些清单

xs
|> List.map transform
//will give you a List<List<Target>>
|> List.concat