【问题标题】:extract N from each group从每组中提取 N
【发布时间】:2017-07-24 08:03:35
【问题描述】:

我有一个包含两列的 csv 文件:“id”和“values”

一个 id 可以在文件中以不同的值出现多次。我想对数据进行分层采样以获得一个较小的文件,其中包含每个 id 的确切或最多 N 个。

示例输入:

    a    1,2,3
    a    2,2,3
    a    2,2,3
    a    2,4,3
    a    4,2,3
    a    4,4,4
    b    3,4,4
    b    8,8,8
    b    3,3,3
    c    4,5,6
    c    5,5,4

期望的输出(N=2):

    a    1,2,3
    a    2,2,3
    b    3,4,4
    b    8,8,8
    c    4,5,6
    c    5,5,4

在这一点上,我不关心组中的哪个 id,但如果是随机的,则加分。

由于文本文件可能非常大,我更喜欢内存高效 (linux) 命令行解决方案(即 bash、awk、sed 等)

【问题讨论】:

  • 请添加您尝试解决此问题的内容...使用awk,您可以使用以 id 为键的数组,如果该键最多被看到 N 次,则打印
  • 感谢您的提示,我对 awk 不是很熟悉。我根据您的建议创建了一个解决方案。

标签: bash awk sed split


【解决方案1】:

这是 awk 中的一个,实现了某种随机性。它读取文件两次。在第一轮中,它计算键并在第二轮中使用 some 一种概率输出记录。没有经过广泛测试,但它应该避免被零除,如果概率之神在此之前没有干预,则返回每个键的最后 2 条记录:

$ awk -v seed=$RANDOM -v n=2 '  # n is the count of keys wanted
BEGIN {
    srand(seed)                 
}
NR==FNR {                       # on the first run
    nc[$1]=n                    # ncound for each key (2)
    c[$1]++                     # count of keys
    next
}
{
    if(nc[$1]>0 && c[$1]>0 && (nc[$1]/c[$1]/(1-rand()))>1) {
        print
        nc[$1]--                # reduce n count for key when printing
    }
    c[$1]--                     # keys left counter reduces at each iteration
}' file file
a    1,2,3
a    4,2,3
b    8,8,8
b    3,3,3
c    4,5,6
c    5,5,4

又一次运行:

a    2,2,3
a    4,2,3
b    3,4,4
b    8,8,8
c    4,5,6
c    5,5,4

( nc[$1] / c[$1] / (1-rand()) ) > 1 这保证了如果之前没有打印任何键,则返回每个键的最后 2 个(或 n)记录,例如 nc=2; c=2 然后 nc/c==11/[0-1[ > 1 总是。 rand() 返回值在[0-1[ 范围内,为了避免/0/(1-rand()) 随机分布可能不均匀。

【讨论】:

  • 很好的答案,并且能够使用种子使其可重现,这真是太棒了。但是,对文件进行两次迭代会使它对我的情况不太实用。
【解决方案2】:

根据@Sundeep 的建议,我使用 shuf 和 awk 和 array[id] 计数构造(对于 N=10)提出了一个解决方案:

shuf ./data.csv | awk '{count[$1]++} {if (count[$1] < 10)print $1, $2}' ./data.csv 

这需要每个 id 的前 10 项。

【讨论】:

  • 对于随机,你可以使用类似shuf data.csv | awk -v n='2' '++seen[$1]&lt;=n' | sort
【解决方案3】:
[akshay@localhost tmp]$ awk -v n=2 '++arr_seen[$1] <=n'  file
    a    1,2,3
    a    2,2,3
    b    3,4,4
    b    8,8,8
    c    4,5,6
    c    5,5,4

输入

[akshay@localhost tmp]$ cat file
    a    1,2,3
    a    2,2,3
    a    2,2,3
    a    2,4,3
    a    4,2,3
    a    4,4,4
    b    3,4,4
    b    8,8,8
    b    3,3,3
    c    4,5,6
    c    5,5,4

【讨论】:

    【解决方案4】:
    $ n=2; awk -v n=$n 'arr[$1]<n{arr[$1]++;print $0}' file
        a    1,2,3
        a    2,2,3
        b    3,4,4
        b    8,8,8
        c    4,5,6
        c    5,5,4
    

    简要说明,

    • n=2:将所需的显示时间设置为 bash 变量 n
    • arr[$1]&lt;n:id 将是数组的键,每个 id 的值将是每个键的值。如果键值为&lt;n,则打印该行,然后打印arr[$1]++

    【讨论】:

      猜你喜欢
      • 2023-02-02
      • 2018-10-13
      • 2013-04-25
      • 2020-11-27
      • 2021-12-13
      • 1970-01-01
      • 2021-12-30
      • 2019-08-03
      • 1970-01-01
      相关资源
      最近更新 更多