【问题标题】:In F#, are there any operations like 'melt' or 'cast' in R?在 F# 中,R 中是否有类似“melt”或“cast”之类的操作?
【发布时间】:2012-03-20 07:41:05
【问题描述】:

Melt 和 Cast 是在 R 中处理数据的常用操作。 在 F# 中,这将是相同类型或类似类型的记录序列。

你知道 F# 中有这样的函数吗?

(如果没有,谁有兴趣制作它们的强类型版本...)

更多信息:

Melt 将表格作为输入。 它有列标题(我们的记录字段)和一系列行。 这些列可以分为一组“标识符”和一组“变量”

Melt 将此表置于新的规范形式中,列现在为: 标识符、名为@"variable" 的列、名为@"value" 的列

如果您最初有 10 个“变量”,例如大小、重量等。对于之前的每条记录,您将拥有 10 条规范形式的记录,@'variable' 列中的值用标题填充您的“变量”中的前几列

铸造,相反,从融化的桌子重建一张桌子。

R 中的一个简短示例 melt 获取如下所示的数据 (dat):

  a          b         c
1 1 0.48411551 0.2372291
2 2 0.58850308 0.3968759
3 3 0.74412592 0.9718320
4 4 0.93060118 0.8665092
5 5 0.01556804 0.2512399

让它看起来像这样:

> melt(dat,id.vars = "a")
   a variable      value
1  1        b 0.48411551
2  2        b 0.58850308
3  3        b 0.74412592
4  4        b 0.93060118
5  5        b 0.01556804
6  1        c 0.23722911
7  2        c 0.39687586
8  3        c 0.97183200
9  4        c 0.86650918
10 5        c 0.25123992

cast 基本上是相反的。

这 2 个操作在日常工作中非常强大。 一旦你拥有它们,它就会改变你的想法,就像 FP 所做的那样。

【问题讨论】:

标签: r f# reshape


【解决方案1】:

假设 melt 类似于 SQL Server 的 unpivot,这应该可以解决问题:

let melt keys (table: DataTable) = 
  let out = new DataTable()
  let keyCols, otherCols = 
    table.Columns
    |> Seq.cast<DataColumn>
    |> Seq.toArray
    |> Array.partition (fun c -> keys |> Seq.exists (fun k -> k = c.ColumnName))
  for c in keyCols do
    out.Columns.Add(c.ColumnName) |> ignore
  out.Columns.Add("Key", typeof<string>) |> ignore
  out.Columns.Add("Value") |> ignore
  for r in table.Rows do
    for c in otherCols do
      let values = [|
        for c in keyCols do yield r.[c]
        yield box c.ColumnName
        yield r.[c]
      |]
      out.Rows.Add(values) |> ignore
  out

这里有一个小测试来试试看:

let table = new DataTable()
[|"Country", typeof<string>
  "2001", typeof<int>
  "2002", typeof<int>
  "2003", typeof<int>|]
|> Array.map (fun (name, typ) -> new DataColumn(name, typ))
|> table.Columns.AddRange

[
  "Nigeria", 1, 2, 3
  "UK", 2, 3, 4
]
|> List.iter (fun (a, b, c, d) -> table.Rows.Add(a, b, c, d) |> ignore)

let table2 = table |> melt ["Country"]

table2.Rows
|> Seq.cast<DataRow>
|> Seq.iter (fun r ->
  for (c: DataColumn) in table2.Columns do
    printfn "%A: %A" c.ColumnName r.[c]
  printfn "")

产生

"Country": "Nigeria"
"Key": "2001"
"Value": "1"

"Country": "Nigeria"
"Key": "2002"
"Value": "2"

...

假设cast 是另一种方式(即pivot),您应该能够获取此代码并提出翻译。

如果您经常这样做,您可能会发现将数据加载到 SQL Server 并使用内置运算符会更容易。

【讨论】:

  • 看起来它也在做同样的事情。虽然我希望(梦想)有一个 F# 风格,但它的严重强类型版本。
  • 您需要列标题才能完成这项工作,因为我不知道您处理的是什么类型,所以我使用了DataTable。我认为您可以采用逻辑并对其进行自定义以使用您喜欢的类型。
  • 我不明白如何轻松地使熔体类型独立但强类型。我们必须走类型提供者的方式吗?
  • Strong Types 比 Girlie Types 好
  • 有很多方法可以解决这个问题:将标题作为单独的序列传递,使用索引而不是列名等。
【解决方案2】:

你知道 F# 中有这样的函数吗?

F# 标准库中没有这样的函数。

R 中的一个简短示例

您的示例数据可以这样用 F# 编写:

let header, data =
  [ "a"; "b"; "c" ],
  [ 1, 0.48411551, 0.2372291
    2, 0.58850308, 0.3968759
    3, 0.74412592, 0.9718320
    4, 0.93060118, 0.8665092
    5, 0.01556804, 0.2512399 ]

然后像这样“融化”:

let melt header data =
  let header, data = Array.ofSeq header, Array.ofSeq data
  [ header.[0], "variable", "value" ],
  [ for a, b, c in data do
      yield a, "b", b
      yield a, "c", c ]

请注意,静态类型要求您的“b”和“c”列包含相同类型的值,因为它们已合并到单个列中。

这 2 个操作在日常工作中非常强大。一旦你拥有它们,它就会改变你的想法,就像 FP 所做的那样。

我不明白为什么。我怀疑这是一个 XY 问题,您正在描述如何在 R 中解决问题,而在 F# 中使用更类型化的方法可以更好地解决相同的问题,例如从“a”的映射到从“变量”到“的映射”价值”,但不知道任何人可能想要这些功能,因为我无法确定。

【讨论】:

    猜你喜欢
    • 2020-10-09
    • 1970-01-01
    • 1970-01-01
    • 2017-01-04
    • 1970-01-01
    • 2019-08-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多