我正在尝试试验来自冠状病毒大流行的实时数据(不幸的是,祝大家好运)。

我已经开发了一个小脚本,并且正在过渡到控制台应用程序:它使用CSV类型的提供程序。

我有以下问题。假设我们要按地区过滤意大利利差,我们可以使用此代码到.fsx文件中:

open FSharp.Data

let provinceData = CsvProvider< @"https://raw.githubusercontent.com/pcm-dpc/COVID-19/master/dati-province/dpc-covid19-ita-province.csv" , IgnoreErrors = true>.GetSample()


let filterDataByProvince province =
    provinceData.Rows
    |> Seq.filter (fun x -> x.Sigla_provincia = province)

由于序列是惰性的,因此假设我强迫编译器将内存中罗马省的数据加载到内存中,我可以添加:
let romeProvince = filterDataByProvince "RM" |> Seq.toArray

由FSI在本地运行,效果很好。

现在,如果我使用.fs文件将此代码转换到控制台应用程序中;我声明完全相同的函数,并使用完全相同的类型提供程序加载程序;但是我没有使用最后一行来收集数据,而是将其放入主函数中:
[<EntryPoint>]
let main _ =
    let romeProvince = filterDataByProvince "RM" |> Seq.toArray

    Console.Read() |> ignore
    0

这导致以下运行时异常:
System.Exception
  HResult=0x80131500
  Message=totale_casi is missing
  Source=FSharp.Data
  StackTrace:
   at <StartupCode$FSharp-Data>.$TextRuntime.GetNonOptionalValue@139-4.Invoke(String message)
   at CoronaSchiatta.Evoluzione.provinceData@10.Invoke(Object parent, String[] row) in C:\Users\glddm\source\repos\CoronaSchiatta\CoronaSchiatta\CoronaEvolution.fs:line 10
   at FSharp.Data.Runtime.CsvHelpers.parseIntoTypedRows@174.GenerateNext(IEnumerable`1& next)

你能解释一下吗?

某些行可能采用奇数格式,但是FSI会话对那些行很健壮,而控制台版本却很脆弱。为什么?我该如何解决?

我正在使用VS2019 Community Edition,目标是.NET Framework 4.7.2,F#运行时:4.7.0.0;
作为FSI,我使用以下软件:FSI Microsoft(R)F#交互式版本10.7.0.0(用于F#4.7)

PS:请注意,如果我使用CsvFile而不是类型提供程序,如下所示:
let test = @"https://raw.githubusercontent.com/pcm-dpc/COVID-19/master/dati-province/dpc-covid19-ita-province.csv"
               |> CsvFile.Load |> (fun  x  -> x.Rows )  |> Seq.filter ( fun x-> x.[6 ] = "RM")
               |> Seq.iter ( fun x -> x.[9] |> Console.WriteLine )

然后,它在控制台应用程序中也像超级按钮一样工作。当然,我想使用类型提供程序,否则我必须添加类型定义,将架构映射到列(这样会更脆弱)。最后一行只是一个快速测试。

最佳答案

易碎性

如果您没有良好的架构或示例,则CSV类型提供程序可能会很脆弱。

现在几乎可以肯定会遇到运行时错误,因为您的数据不匹配。
您如何解决?一种方法是首先运行数据:

provinceData.Rows |> Seq.iteri (fun i x -> printfn "Row %d: %A" (i + 1) x)

这将运行到第2150行。毫无疑问,下一行:
2020-03-11 17:00:00,ITA,19,Sicilia,994,In fase di definizione/aggiornamento,,0,0,

您可以看到缺少最后一个值(totale_casi)。

CsvProvider的选项之一是InferRows。这是提供程序扫描以建立模式的行数,其默认值恰好是1000。

所以:
type COVID = CsvProvider<uri, InferRows = 0>

防止将来发生这种情况的更好方法是从数据子集中手动定义样本:
type COVID = CsvProvider<"sample-dpc-covid19-ita-province.csv">
sample-dpc-covid19-ita-province.csv是:
    data,stato,codice_regione,denominazione_regione,codice_provincia,denominazione_provincia,sigla_provincia,lat,long,totale_casi
    2020-02-24 18:00:00,ITA,13,Abruzzo,069,Chieti,CH,42.35103167,14.16754574,0
    2020-02-24 18:00:00,ITA,13,Abruzzo,066,L'Aquila,AQ,42.35122196,13.39843823,
    2020-02-24 18:00:00,ITA,13,Abruzzo,068,Pescara,PE,42.46458398,14.21364822,0
    2020-02-24 18:00:00,ITA,13,Abruzzo,067,Teramo,TE,42.6589177,13.70439971,0

这样,totale_casi的类型现在为Nullable<int>

如果您不介意NaN值,也可以使用:
CsvProvider<..., AssumeMissingValues = true>

为什么FSI看起来更强大?

FSI没有更强大。这是我最好的猜测:

您的架构源正在定期更新。
键入Providers cache the schema,这样它就不会在每次编译代码时都重新生成架构,这可能是不切实际的。重新启动FSI会话时,最终将重新生成类型提供程序,但控制台应用程序则无法。因此,使用更新的源有时可能会减少错误的发生。

07-24 13:12