使用 Deedle 设置缺失值

Set missing value with Deedle

假设我有一个两步过程。第一个数据 collection/cleaning 和第二个一些操作。

例如:

#r "nuget: Deedle"

open Deedle

type Person = 
    { Name:string; Birthday:DateTime}

let fixB b =
    if b > DateTime(2023,01,01) then OptionalValue.Missing else OptionalValue b

let peopleRecds = [ { Name = "Joe"; Birthday = DateTime(9999,12,31) }
                    { Name = "Jim"; Birthday = DateTime(2000,12,31) }]

let df = Frame.ofRecords peopleRecds

let step1 = df.Clone()

step1.ReplaceColumn("Birthday", df |> Frame.mapRowValues (fun row -> fixB (row.GetAs<DateTime>"Birthday")))

step1.SaveCsv(__SOURCE_DIRECTORY__ + "step1.csv")

let step1' = Frame.ReadCsv(__SOURCE_DIRECTORY__ + "step1.csv")

step1.Print()
     Name Birthday               
0 -> Joe  <missing>              
1 -> Jim  12/31/2000 12:00:00 AM 

如果我保存它(step1')或不保存(step1),我想继续而不必处理step2中的不同情况。

let payout b  =
    match b with
    | OptionalValue.Present c -> if c > DateTime(2000,01,01) then 100 else 0
    | OptionalValue.Missing -> 0 

let step2 = step1.Clone()
step2.AddColumn("Payout", step1 |> Frame.mapRowValues (fun row -> payout (row.TryGetAs<DateTime>"Birthday")))
Error: System.InvalidCastException: Object must implement IConvertible.

第一个问题是您使用 mapRowValues 的方式在数据框中引入了可选值(这通常会自动消除,但在这种情况下似乎不会)。 OptionValue<'T> 没有实现 IConvertible,所以这稍后会导致问题。您可以通过如下计算生日来解决此问题:

let fixB b =
    if b > DateTime(2023,01,01) then None else Some b

let bday = 
  df.Columns.["Birthda  y"].As<DateTime>() 
  |> Series.mapAll (fun _ v -> Option.bind fixB v)

step1.ReplaceColumn("Birthday", bday)

保存和加载数据框的第二个问题是 CSV 解析器似乎无法自动计算出 BirthdayDateTime。您可以通过添加一个显式模式来解决这个问题(您也可以禁用密钥保存以确保您加载的帧与您保存的帧完全相同):

step1.SaveCsv(__SOURCE_DIRECTORY__ + "step1.csv",includeRowKeys=false)
let step1' = Frame.ReadCsv(__SOURCE_DIRECTORY__ + "step1.csv", schema="string,date")