You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Deedle按列去重并保留每个重复键的首次出现行

Deedle数据框按列去重保留首次出现行

问题场景

存在重复键值的数据表,需要删除重复键对应的冗余行,仅保留每个重复键首次出现的第一行。
初始读取数据代码:

let data = "A;B\na;1\nb;\nb;2\nc;3"

let bytes = System.Text.Encoding.UTF8.GetBytes data
let stream =  new MemoryStream( bytes )

let df= 
    Frame.ReadCsv(
        stream = stream,
        separators = ";",
        hasHeaders = true
    )

df.Print()

原始数据输出:

A B         
0 -> a 1         
1 -> b <missing> 
2 -> b 2         
3 -> c 3              

期望输出:

A B         
0 -> a 1         
1 -> b <missing>       
2 -> c 3       

之前使用applyLevel的实现得到了错误结果,重复键b保留了第二行而非首次出现的首行:

let df1 =
    df
    |> Frame.groupRowsByString "A"
    |> Frame.applyLevel fst (fun s -> s |> Series.firstValue)

df1.Print()

错误输出:

A B 
a -> a 1 
b -> b 2 <- wrong
c -> c 3 

错误原因

Series.firstValue取的是序列按键排序后的第一个值,而非数据原始加载顺序的第一个值。分组后缺失值<missing>的排序优先级低于正常值,因此b分组中会优先取到B=2的行,不符合需求。

正确实现

直接按原始行顺序遍历去重列,记录已经出现过的键,保留每个键第一次出现的行索引,最后按索引筛选数据框即可,全程保留原始数据顺序,不需要额外排序:

let dfDedup =
    // 遍历去重键列,标记每个值是否首次出现
    df.GetColumn<string>("A")
    |> Series.scanValues (fun (seen, _) key ->
        if Set.contains key seen then (seen, None)
        else (Set.add key seen, Some key)
    ) (Set.empty<string>, None)
    // 提取所有首次出现的行对应的原始索引
    |> Series.values
    |> Seq.choose snd
    |> Seq.zip df.RowKeys
    |> Seq.map fst
    // 按索引筛选行,重置为连续序号索引
    |> fun keepRows -> df.Rows.[keepRows]
    |> Frame.indexRowsOrdinally

dfDedup.Print()

运行后输出完全符合预期:

A B         
0 -> a 1         
1 -> b <missing>       
2 -> c 3       

如果不需要重置行索引,去掉最后一行|> Frame.indexRowsOrdinally即可,此时会保留原始行号0、1、3。


内容的提问来源于stack exchange,提问作者jim108dev

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 01:06:25