4

如何访问 csv 文件中的条目以便在 F# 中对它们执行计算?

我可以以通常的方式将 csv 文件读入内存,但是一旦我被卡住了。

理想情况下,我会从列创建数组,然后使用 array.map2 执行计算。

所以我的数组 1 是一些网站使用指标,第 2 列是达到第 1 列中的值的用户数(比如对网站进行了 6 次访问),我们可以通过将数组中的每个条目相乘来计算平均访问次数第 1 列,由第 2 列组成的数组除以第 2 列的 array.sum。

我已经在 F# 片段http://fssnip.net/3T上尝试了 csv to Array 代码,但它为我生成了一系列字符串元组的数组。

任何人都可以提出更好的方法吗?

编辑:一些示例输入将与此类似:-

     Visits Count
     1  8
     2  9
     3  5
     4  3
     5  2
     6  1
     7  1
    10  1

输出将返回数据的平均值,在本例中为 2.87(保留 2 个小数位)。

编辑 2:我发现的从 CSV 到数组代码的当前输出是这样的

     val it : seq<BookWindow> =
            seq [{Visits = 1;
                  Count = 8;}; {Visits = 2;
                           Count = 9;}; {Visits = 3;
                                  Count = 5;}; {Visits = 4;
                                              Count = 3;}; ...]

这对计算不是那么有用......

4

3 回答 3

6

值得补充的是,使用 F# 3.0 类型提供程序,访问 CSV 文件变得更加简单。类型提供者可以在编译期间静态查看 CSV 数据并生成表示列的类型(如BookWindow),然后推断各个列的数据类型。

例如,请查看新版 Try F# 网站“财务建模”下的“使用 Yahoo Finance Type Provider”一文。您可以编写如下内容:

#r "Samples.Csv.dll"

// Type provider that generates schema based on CSV file located online
[<Literal>]
let url = "http://ichart.finance.yahoo.com/table.csv?s=MSFT"
let msft = new Samples.FSharp.CsvProvider.MiniCsv<url>()

// The provider automatically infers the structure and we
// can access columns as properties of the 'row' object
for row in msft.Data do
  printfn "%A %f" row.Date row.Close

据我所知,CSV 提供程序的最新公开版本位于F# 3.0 Sample Pack中。我有一个可能更好的版本,它还可以在我的 GitHub 存储库上处理类型推断。

将数据存储在内存中后,您可以使用标准 F# 函数进行计算。例如,要计算平均收盘价(你可以在 Try F# 上尝试),你可以这样写:

 Seq.average [ for row in msft.Data -> row.Close ]

这会生成一个仅包含收盘价的列表,然后在数字上调用标准平均函数。

于 2012-10-23T13:31:16.213 回答
6

我所做的是创建一个记录类型,以便稍后可以使用强类型操作,然后seq<myRecord>像下面的代码一样快速将文本文件读入。如果我打算稍后重用它,我通常将方法移动到记录中static member fromFile。如果您像往常一样使用大型文本文件,则 seq 非常有用,这种方式使用的内存非常少。

编辑这更干净:

open System.IO

type myRecord = { 
    Visits: int
    Count: int 
} with
    static member fromFile file = 
        file
        |> File.ReadLines       // expose as seq<string>
        |> Seq.skip 1           // skip headers
        |> Seq.map (fun s-> s.Split '\t') // split each line into array
        |> Seq.map (fun a -> {Visits=int a.[0]; Count=int a.[1]}) // and create record

myRecord.fromFile @"D:\data.csv"
|> Seq.fold (fun (tv, tc) r -> (tv+r.Visits*r.Count, tc+r.Count))(0,0)
|> (fun t -> float (fst t) / float (snd t))
//val mean : float = 2.866666667
于 2012-10-23T10:49:25.367 回答
2

您可能使事情过于复杂,这不是最干净的解决方案,但是您仍然可以使用现有的东西。如果这提供了一种进行计算的好方法,则将该 BookWindow 类型映射到单独的数组中。

 type BookWindow = { Visits: int
                     Count: int }
 // Sample data
 let list = [|{Visits = 1; Count = 8;}; {Visits = 2; Count = 9;}; {Visits = 3; Count = 5;}|]

 let visitcol = list |> Array.map (fun r -> r.Visits)
 let countcol = list |> Array.map (fun r -> r.Count)
 Array.map2( fun v c -> v * c) visitcol countcol
于 2012-10-23T10:13:56.790 回答