如何在 F# 中优雅地操作此 Deedle.Frame
How do I elegantly manipulate this Deedle.Frame in F#
我一直在努力使用 F#,尤其是使用 Deedle.Frame 和 Deedle.Series。
- 我需要创建两个新列,其值依赖于其他列。
- 对于某些行,我需要插入一个新行。
我目前的解决方案非常难看,基本上是根据每个 if 语句过滤框架,return 一个仅包含新列的新框架。然后我将其合并到原始框架中。这个工程自例还是互斥的。当我需要插入新行时,我将重新索引新框架,以便在合并期间将其简单地附加在原始框架的底部(最后一行之后)。
但是,由于不同的 if-cases/patterns 很多,我必须处理大量的帧合并,并确保处理 IF 语句的补码(否则会有缺失值) .
// ****** 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.
任何能够提出好的解决方案的人都可以获得金星奖。我想这可以通过 returning 帧列表(因为可能会创建一个新行)然后展平并合并所有来解决?
***** 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
我会尝试这样的事情
先定义几个类型,让东西好办一些
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()
定义变换函数。
诀窍是这个函数 returns 一个 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
我一直在努力使用 F#,尤其是使用 Deedle.Frame 和 Deedle.Series。
- 我需要创建两个新列,其值依赖于其他列。
- 对于某些行,我需要插入一个新行。
我目前的解决方案非常难看,基本上是根据每个 if 语句过滤框架,return 一个仅包含新列的新框架。然后我将其合并到原始框架中。这个工程自例还是互斥的。当我需要插入新行时,我将重新索引新框架,以便在合并期间将其简单地附加在原始框架的底部(最后一行之后)。
但是,由于不同的 if-cases/patterns 很多,我必须处理大量的帧合并,并确保处理 IF 语句的补码(否则会有缺失值) .
// ****** 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.
任何能够提出好的解决方案的人都可以获得金星奖。我想这可以通过 returning 帧列表(因为可能会创建一个新行)然后展平并合并所有来解决?
***** 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
我会尝试这样的事情
先定义几个类型,让东西好办一些
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()
定义变换函数。
诀窍是这个函数 returns 一个 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