【问题标题】:How to select 10 thousand random lines from a 44 million lines file如何从 4400 万行文件中随机选择 10000 行
【发布时间】:2016-04-29 07:48:14
【问题描述】:

我尝试使用shuf 来打乱文件,但耗时太长;该进程被托管管理员杀死。我有最便宜的 Linux Bluehost 计划。

shuf MMM.csv

文件有 44M 行,文件大小为 7439641823 字节,使用sort -R 更糟糕,考虑将文件分成 44 个文件,但不会很随机,任何想法将不胜感激

我想要的是打乱文件,然后提取前 10000 行

文件已排序,由于业务原因,10000行无法排序

【问题讨论】:

  • 嗯。我可以为此想到一些不同的算法——一种是生成一个 4400 万长的随机数或 UUID 列表,并将该列表用作排序键;可以使用该键对单个拆分文件进行排序(索引到完整列表中以获取适当的偏移量),然后执行合并排序传递。然而,在进入细节之前——实际的用例是什么?它实际上需要有多随机?
  • 您这样做是为了达到什么目的?这在我看来像是一个 XY 问题。
  • 正如 Sobrique 所说,这可以通过了解您的最终目的进行最佳优化。 shuffle 本身永远不会结束,通常用于从样本中选择随机子集。你用你的 shuffle 做什么?
  • 您的编辑完全改变了问题。您需要做的就是从文件中随机选择 10,000 行;你不需要洗牌整个 4400 万行。这就是@Sobrique 关于“XY 问题”的意思。
  • 然后发布一个问题说,不是你必须洗牌一个44M的行文件!另外发布一个示例,其中包含一个 10 行长的文件,并询问如何从该文件打印 4 行并显示预期的输出。

标签: linux bash perl sorting awk


【解决方案1】:

关键是使用“shuf”和 -n(“最多输出 COUNT 行”)选项。

比较:

$ time (seq 1 44000000 | shuf > /tmp/shuffled)
user  0m58.234s
sys   0m4.394s

$ time (seq 1 44000000 | shuf -n 10000 > /tmp/shuffled)
user   0m25.493s
sys    0m1.771s

(这些计时是在一台令人遗憾的旧 2.53GHz Mac 上拍摄的。)

注意:在某些环境中,“shuf”可能以“gshuf”的形式提供。

【讨论】:

    【解决方案2】:

    鉴于您从文件中打印一些固定数量的随机行的新要求:

    $ cat tst.awk
    NR==1 {
        srand()
        for (i=1;i<=outNum;i++) {
            if (tgts[int(rand()*inNum)+1]++) {
                i--
            }
        }
    }
    NR in tgts
    
    $ seq 44000000 > file44m
    
    $ time awk -v inNum=$(wc -l < file44m) -v outNum=10000 -f tst.awk file44m > file10k
    real    0m17.676s
    user    0m17.238s
    sys     0m0.404s
    
    $ sort -u file10k | wc -l
    10000
    

    以上仅将outNum 行号存储在内存中,因此应该没有内存问题。请参阅下文了解它在小文件上的工作原理:

    $ cat file
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    
    $ awk -v inNum=$(wc -l < file) -v outNum=4 -f tst.awk file
    6
    8
    9
    10
    
    $ awk -v inNum=$(wc -l < file) -v outNum=4 -f tst.awk file
    1
    6
    7
    9
    
    $ awk -v inNum=$(wc -l < file) -v outNum=3 -f tst.awk file
    3
    7
    8
    
    $ awk -v inNum=$(wc -l < file) -v outNum=3 -f tst.awk file
    4
    5
    6
    

    【讨论】:

    • 您是否将整个 7GB 文件读取了两次,以便获得第一遍的记录数?这很勤奋:-)
    • 原来使用wc -l 提前计算行数使脚本明显更快(17 秒而不是 31 秒的执行时间)所以我更新了我的答案。感谢您的提示。
    【解决方案3】:

    我决定使用:

    perl -ne 'print if (rand() < .001)' MMM.csv > MMM.out
    

    并从中获取 10000 个子集

    但我仍然想要在 10 秒内随机播放 44M 行文件的解决方案,这甚至可以在共享主机帐户上实现吗?

    【讨论】:

    • “我想要一辆法拉利,但我只有一辆 1983 年的 Datsun。我怎样才能以不同的方式驾驶我的 Datsun 以达到与法拉利相同的性能水平?”在问题上投入更多硬件并不总是正确的解决方案,但这次是。
    • 甚至不是 Datsun,这是一辆 1990 年代的旧自行车,甜心!
    • $ cat /proc/meminfo MemTotal:32946460 kB MemFree:818016 kB 缓冲区:2797180 kB 缓存:19553908 kB SwapCached:427952 kB 活动:15633248 kB 非活动:11417452 kB 2 活动(an) (匿名):3517568 kB 活动(文件):10462876 kB 非活动(文件):7899884 kB 不可撤销:25856 kB 锁定:25880 kB SwapTotal:8388604 kB SwapFree:5296460 kB 脏:3272 kB 回写:0 kB AnonPages:4 563 kB 384584 kB Shmem:3984020 kB
    • 这是一个共享服务器,所以我只得到一个切片,当达到某个阈值时,可能内核恶魔会把我踢出去
    【解决方案4】:

    这是我大约一年前写的。请让我知道它在你的测试中是如何进行的。如果您的输入已排序,但您不希望结果排序,则 shuf 输出。

    在我看来,您的输入到输出大小已经足够大,您希望将渐进式跳过部分取消注释并正常工作。我不需要它,所以据我记得它未经测试。

    #! /usr/bin/awk -F
    
    # reservoir_sample.awk
    
    # the basic reservoir algorithm is due to Alan Waterman (according to Knuth) 
    # Vitter (85) improved timing and made sampling uniform
    # http://www.cs.umd.edu/~samir/498/vitter.pdf
    # 
    # Expect a K parameter which is the size of the intended sample
    # 
    # reservoir_sample.awk -v K=1000   population.list
    
    
    BEGIN {
        # give srand a fixed seed for reproducubility
        # or a variable for diversity    
        srand(systime() + PROCINFO["pid"]); 
    
        # 23 is a magic number between 10 & 40 as per Vitter  
        threshold = 23 * K; 
    }
    
    # fill the reservoir
    NR <= K {   reservoir[NR] = $0 }
    
    # replace item in resovior with current item 
    # on probability of K / (NR) 
    NR > K {
        uniform_probability = int(rand() * NR + 0.5);
        if (uniform_probability  <= K) 
            reservoir[uniform_probability] = $0
    
        # when the population is large with respect to sample size K
        # test fewer items from the population for inclusion in the reservoir   
        #if(NR < threshold){
        #   skip = 
        #   for (i=0 ; i < skip; i++) getline()  
        #}
    }
    
    # and Bobs yer uncle
    END {   for(item in reservoir) {print reservoir[item]} }
    
    # without the progressivly larger steps through the population
    # this would not approach uniform since for larger the populations 
    # more earlier candidates are replaced more than later candidates.
    

    【讨论】:

      猜你喜欢
      • 2012-09-03
      • 2012-03-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-06-11
      • 2020-10-18
      • 2018-04-03
      相关资源
      最近更新 更多