【问题标题】:Working with heterogenous data in a statically typed language (F#)使用静态类型语言 (F#) 处理异构数据
【发布时间】:2009-11-25 18:15:36
【问题描述】:

F# 的一项声明是它允许交互式脚本和数据操作/探索。我一直在玩 F#,试图了解它如何与 Matlab 和 R 进行数据分析工作的比较。显然 F# 不具备这些生态系统的所有实用功能,但我更感兴趣的是底层语言的一般优点/缺点。

对我来说,最大的变化,即使是函数式风格,也是 F# 是静态类型的。这有一些吸引力,但也经常感觉像一件直筒夹克。例如,我还没有找到一种方便的方法来处理异构矩形数据——想想 R 中的数据框。假设我正在读取一个包含名称(字符串)和权重(浮点数)的 CSV 文件。通常我会加载数据、执行一些转换、添加变量等,然后运行分析。在 R 中,第一部分可能如下所示:

df <- read.csv('weights.csv')
df$logweight <- log(df$weight)

在 F# 中,不清楚我应该使用什么结构来执行此操作。据我所知,我有两个选择:1)我可以先定义一个强类型的类(Expert F# 9.10),或者 2)我可以使用异构容器,例如 ArrayList。静态类型的类似乎不可行,因为我需要在加载数据后以临时方式(对数权重)添加变量。异构容器也很不方便,因为每次访问变量时都需要将其拆箱。在 F# 中:

let df = readCsv("weights.csv")
df.["logweight"] = log(double df.["weight"])

如果这是一次或两次,那可能没问题,但是在我使用变量时每次指定一个类型似乎并不合理。我经常处理包含 100 个变量的调查,这些变量被添加/删除、拆分为新的子集并与其他数据框合并。

我错过了一些明显的第三选择吗?是否有一些有趣且轻松的方式来交互和操作异构数据?如果我需要在 .Net 上进行数据分析,我目前的感觉是我应该使用 IronPython 进行所有数据探索/转换/交互工作,并且只使用 F#/C# 进行数值密集型部分。 F# 天生就不是用于快速和肮脏的异构数据工作的错误工具吗?

【问题讨论】:

  • 很好,明确的问题。我期待着答案。

标签: c# .net f#


【解决方案1】:

F# 天生就是错误的工具吗? 快速而肮脏的异构数据 工作吗?

对于完全即席的探索性数据挖掘,我不推荐 F#,因为类型会妨碍您。

但是,如果您的数据定义得非常好,那么您可以通过将所有类型映射到一个通用 F# 联合来将不同的数据类型保存在同一个容器中:

> #r "FSharp.PowerPack";;

--> Referenced 'C:\Program Files\FSharp-1.9.6.16\bin\FSharp.PowerPack.dll'

> let rawData =
    "Name: Juliet
     Age: 23
     Sex: F
     Awesome: True"

type csv =
    | Name of string
    | Age of int
    | Sex of char
    | Awesome of bool

let parseData data =
    String.split ['\n'] data
    |> Seq.map (fun s ->
        let parts = String.split [':'] s
        match parts.[0].Trim(), parts.[1].Trim() with
        | "Name", x -> Name(x)
        | "Age", x -> Age(int x)
        | "Sex", x -> Sex(x.[0])
        | "Awesome", x -> Awesome(System.Convert.ToBoolean(x))
        | data, _ -> failwithf "Unknown %s" data)
    |> Seq.to_list;;

val rawData : string =
  "Name: Juliet
     Age: 23
     Sex: F
     Awesome: True"
type csv =
  | Name of string
  | Age of int
  | Sex of char
  | Awesome of bool
val parseData : string -> csv list

> parseData rawData;;
val it : csv list = [Name "Juliet"; Age 23; Sex 'F'; Awesome true]

csv list 是强类型的,您可以对其进行模式匹配,但您必须预先定义所有联合构造函数。

我个人更喜欢这种方法,因为它比使用无类型的 ArrayList 好几个数量级。但是,我不太确定您的要求是什么,而且我不知道表示临时变量的好方法(除了可能是 Map{string, obj})所以 YMMV。

【讨论】:

    【解决方案2】:

    我认为还有其他一些选择。

    (?)运营商

    正如 Brian 提到的,您可以使用 (?) 运算符:

    type dict<'a,'b> = System.Collections.Generic.Dictionary<'a,'b>
    
    let (?) (d:dict<_,_>) key = unbox d.[key]
    let (?<-) (d:dict<_,_>) key value = d.[key] <- box value
    
    let df = new dict<string,obj>()
    df?weight <- 50.
    df?logWeight <- log(df?weight)
    

    这确实在每次访问时使用装箱/拆箱,有时您可能需要添加类型注释:

    (* need annotation here, since we could try to unbox at any type *)
    let fltVal = (df?logWeight : float)
    

    顶级标识符

    另一种可能性是,您可以只使用顶级标识符,而不是在现有对象上动态定义属性(F# 支持的不是特别好)。

    let dfLogWeight = log(dfWeight)
    

    这样做的好处是您几乎不需要指定类型,尽管它可能会弄乱您的顶级命名空间。

    属性对象

    需要更多类型和更丑陋语法的最后一个选择是创建强类型的“属性对象”:

    type 'a property = System.Collections.Generic.Dictionary<obj,'a>
    
    let createProp() : property<'a> = new property<'a>()
    let getProp o (prop:property<'a>) : 'a = prop.[o]
    let setProp o (prop:property<'a>) (value:'a) = prop.[o] <- value
    
    let df = new obj()
    let (weight : property<double>) = createProp()
    let (logWeight : property<double>) = createProp()
    
    setProp df weight 50.
    setProp df logWeight (getProp df weight)
    let fltVal = getProp df logWeight
    

    这需要显式创建每个属性(并且此时需要类型注释),但之后不需要类型注释。我发现这比其他选项的可读性要低得多,尽管定义一个运算符来替换 getProp 可能会有所缓解。

    【讨论】:

      【解决方案3】:

      我不确定 F# 是否是一个很棒的工具。但是还有第三种选择——问号运算符。一段时间以来,我一直想写博客; Luca's recent PDC talk 使用 C# 'dynamic' 演示了一个 CSV 阅读器,我想使用 (?) 运算符用 F# 编写类似的代码。见

      F# operator "?"

      简要说明。你可以尝试着自己动手玩这个,或者等我写博客。我自己没有认真尝试过,所以我不确定它的效果如何。

      编辑

      我应该补充一点,Luca 的演讲展示了 C# 中的“动态”如何解决该语言的至少一部分问题。

      编辑

      另见

      http://cs.hubfs.net/forums/thread/12622.aspx

      我在这里发布了一些基本的入门 CSV 代码。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-10-29
        • 1970-01-01
        • 2010-11-22
        • 2016-09-26
        • 1970-01-01
        相关资源
        最近更新 更多