F#、Deedle 和 OptionalValue:对象必须实现 IConvertible 错误

F#, Deedle and OptionalValue: Object must implement IConvertible error

当我尝试在框架中创建缺失值并稍后对它们执行操作时,我遇到了麻烦。这是一个 "working" 示例:

open Deedle
open System.Text.RegularExpressions

do fsi.AddPrinter(fun (printer:Deedle.Internal.IFsiFormattable) -> "\n" + (printer.Format()))

module Frame = let mapAddCol col f frame = frame |> Frame.addCol col (Frame.mapRowValues f frame)

[   {|Desc = "A - 1.50ml"; ``Price ($)`` = 23.|}
    {|Desc = "B - 2ml"; ``Price ($)`` = 18.5|}
    {|Desc = "C"; ``Price ($)`` = 25.|}             ]
|> Frame.ofRecords
(*
     Desc       Price ($) 
0 -> A - 1.50ml 23        
1 -> B - 2ml    18.5      
2 -> C          25        
*)
|> Frame.mapAddCol "Volume (ml)" (fun row ->
    match Regex.Match(row.GetAs<string>("Desc"),"[\d\.]+").Value with
    | "" -> OptionalValue.Missing
    | n -> n |> float |> OptionalValue)
(* 
     Desc       Price ($) Volume (ml) 
0 -> A - 1.50ml 23        1.5         
1 -> B - 2ml    18.5      2           
2 -> C          25        <missing>   
*)
|> fun df -> df?``Price ($/ml)`` <- df?``Price ($)`` / df?``Volume (ml)``
//error message: System.InvalidCastException: Object must implement IConvertible.

这种方法有什么问题?

Deedle 在内部存储一个标志,判断值是否存在。这通常通过 OptionalValue 类型公开,但内部表示实际上并未使用此类型。

当您使用 mapRowValues 等函数生成新数据时,Deedle 需要识别丢失了哪些数据。这种情况仅在有限的情况下发生。当您 return OptionalValue<float> 时,Deedle 实际上会生成一个值类型为 OptionalValue<float> 而不是 float 的序列(类型系统不允许它做任何其他事情)。

对于float的值,解决办法就是将returnnan作为你的缺失值:

|> Frame.mapAddCol "Volume (ml)" (fun row ->
    match Regex.Match(row.GetAs<string>("Desc"),"[\d\.]+").Value with
    | "" -> nan
    | n -> n |> float )

这将创建一系列新的 float 值,然后您可以使用 ? 运算符访问这些值。