在Linux中,如何有效地从大文件中取出10%的随机行?

在Linux中,如何有效地从大文件中取出10%的随机行?,linux,algorithm,bash,sorting,random,Linux,Algorithm,Bash,Sorting,Random,我想随机输出一个文件总行数的10%。例如,文件a有1000000行,然后我想随机输出100000行,其中100000行是1000000的10% 假设文件很小,有一个简单的方法: randomLine=`wc -l a | awk '{printf("%d\n",($1/10))}'` sort -R a | head -n $randomLine 但是使用sort-R非常慢。它将执行专门的随机计算。我的文件有10000000行。排序花费太多时间。是否有任何方法可以归档一个不那么专用、不那么随机

我想随机输出一个文件总行数的10%。例如,文件a有1000000行,然后我想随机输出100000行,其中100000行是1000000的10%

假设文件很小,有一个简单的方法:

randomLine=`wc -l a | awk '{printf("%d\n",($1/10))}'`
sort -R a | head -n $randomLine
但是使用sort-R非常慢。它将执行专门的随机计算。我的文件有10000000行。排序花费太多时间。是否有任何方法可以归档一个不那么专用、不那么随机但高效的采样

编辑想法:

每十行取样一行是可以接受的。但我不知道如何使用shell脚本来实现这一点。 逐行阅读,如有必要

echo $RANDOM%100 | bc
大于20的输出线使用大于10的数字,以确保获得不小于10%的线,一旦输出10%的线,则停止。但我不知道如何使用shell脚本逐行阅读

编辑说明


我想使用shell脚本的原因是我的文件包含\r个字符。文件中的新行字符应该是\n,但Python和Java中的readline函数将\r和\n视为新行字符,这不符合我的需要。

我认为这是最好的方法:

file=your file here
lines_in_file=`wc -l < $file`
lines_wanted=$(($lines_in_file/10))

shuf -n $lines_wanted $file
。。要获得一个介于1和100000之间的随机数,正如下面评论中指出的,它不是1到100000之间的任何数字,而是介于1和100000之间的32768个可能数字中的一个,所以它是一种投影

因此:


我认为这是最好的方式:

file=your file here
lines_in_file=`wc -l < $file`
lines_wanted=$(($lines_in_file/10))

shuf -n $lines_wanted $file
。。要获得一个介于1和100000之间的随机数,正如下面评论中指出的,它不是1到100000之间的任何数字,而是介于1和100000之间的32768个可能数字中的一个,所以它是一种投影

因此:


让我们创建一个从1到Y的随机X数列表。您可以使用:

shuf -i 1-Y -nX
就你而言

shuf -i 1-1000000 -n10000
然后将其存储在分隔的变量空间中,并传递给awk,以便打印这些行号:

awk 'FNR==NR {a[$1]; next} {if (FNR in a) print}' <(shuf -i 1-1000000 -n10000) file
改善 根据建议:


让我们创建一个从1到Y的随机X数列表。您可以使用:

shuf -i 1-Y -nX
就你而言

shuf -i 1-1000000 -n10000
然后将其存储在分隔的变量空间中,并传递给awk,以便打印这些行号:

awk 'FNR==NR {a[$1]; next} {if (FNR in a) print}' <(shuf -i 1-1000000 -n10000) file
改善 根据建议:


我有一个脚本,它会给你大概1/x的行数

#!/usr/bin/perl -w

use strict;

my $ratio = shift;

while (<>) {
    print if ((rand) <= 1 / $ratio);
}


我有一个脚本,它会给你大概1/x的行数

#!/usr/bin/perl -w

use strict;

my $ratio = shift;

while (<>) {
    print if ((rand) <= 1 / $ratio);
}


只需将该文件作为输入运行此awk脚本

BEGIN { srand() }{ if (rand() < 0.10) print $0; }
我已经有一段时间没有使用awk了,但我相信它应该可以

事实上,它确实像预期的那样工作。大约10%的线路是输出的。在使用GNU awk的Windows计算机上,我运行了:

awk "BEGIN { srand() }{ if (rand() < 0.10) print $0; }" <numbers.txt >nums.txt
numbers.txt包含数字1到1000000,每行一个。在多次运行中,文件nums.txt通常包含约100200项,计算结果为10.02%


如果awk认为行的内容有问题,您可以随时更改记录分隔符。即RS=\n;但这应该是Linux机器上的默认设置。

只需将该文件作为输入运行此awk脚本即可

BEGIN { srand() }{ if (rand() < 0.10) print $0; }
我已经有一段时间没有使用awk了,但我相信它应该可以

事实上,它确实像预期的那样工作。大约10%的线路是输出的。在使用GNU awk的Windows计算机上,我运行了:

awk "BEGIN { srand() }{ if (rand() < 0.10) print $0; }" <numbers.txt >nums.txt
numbers.txt包含数字1到1000000,每行一个。在多次运行中,文件nums.txt通常包含约100200项,计算结果为10.02%

如果awk认为行的内容有问题,您可以随时更改记录分隔符。即RS=\n;但这应该是Linux机器上的默认设置。

这里有一种方法可以编辑idea 1。在:

有点慢,虽然它比我机器上的sort-R方法快2.5倍

我们使用readarray从输入流中一次读取10行数据到一个数组中。然后我们使用$RANDOM的最后一位作为该数组的索引,并打印结果行

使用readarray/printf组合应该确保\r字符未经修改地通过,如编辑要求中所述。

以下是执行编辑idea 1的一种方法。在:

有点慢,虽然它比我机器上的sort-R方法快2.5倍

我们使用readarray从输入流中一次读取10行数据到一个数组中。然后我们使用$RANDOM的最后一位作为该数组的索引,并打印结果行


使用readarray/printf组合符应确保\r字符未经修改地通过,如编辑要求中所述。

1000不是1000000的10%:|是否足够随机地从每10个字符中打印一行?如果您想了解一般情况;看看水库取样。没有线索,但是如何将其改编为shell脚本。谢谢。1000000的10%是100000!这不应该在shell中完成。如果你坚持,请逐行阅读,每次都会得到一个均匀分布的随机数。选择一个阈值,使90%的随机数为b

在阈值以下,可能是一些模量m。仅当随机数超过阈值时才打印每行。如果您正好需要10%,请在[1…行数]上进行分发,其中90%的行数低于阈值。。。你不想在shell1000中这样做,1000不是1000000的10%:|它是否足够随机,可以从每10行中随机打印一行?如果你在寻找一个总体概念;看看水库取样。没有线索,但是如何将其改编为shell脚本。谢谢。1000000的10%是100000!这不应该在shell中完成。如果你坚持,请逐行阅读,每次都会得到一个均匀分布的随机数。选择一个阈值,使90%的随机数低于该阈值,可能是某个模m。仅当随机数超过阈值时才打印每行。如果您正好需要10%,请在[1…行数]上进行分发,其中90%的行数低于阈值。。。您不想在Shell中这样做这是一个优雅而直接的解决方案,但是您确定使用sed对文件执行100k传递比在完整文件中进行一次排序读取并将其洗牌更快吗?我确定,因为我刚刚尝试过,而且速度确实很快。对sort也做了同样的操作,而且花费的时间太长,我只是按CTRL+C组合键。理想的做法是要求sed一次提取所有选定的行。但我不知道怎么做。另外,我喜欢我的解决方案,因为你从第一秒就看到了线,你可以随时中断。$RANDOM*100000/32767+1只能产生32768个介于1和100000之间的数字,而不是整个范围。你是对的。。。我再也不能随意榨取美元了!<:考虑到马库斯的问题:……不那么专注,也不是那么随机,而是高效的抽样……刚刚发现舒夫是为我做的!相应地编辑了我的答案,尽管我没有删除前面的方法,因为我认为这是一个有用的练习。这是一个优雅而直接的解决方案,但是你确定用sed对文件执行100k传递比在完整文件中进行一次排序读取并将其洗牌更快吗?我确定,因为我刚刚尝试过,而且速度确实很快。对sort也做了同样的操作,而且花费的时间太长,我只是按CTRL+C组合键。理想的做法是要求sed一次提取所有选定的行。但我不知道怎么做。另外,我喜欢我的解决方案,因为你从第一秒就看到了线,你可以随时中断。$RANDOM*100000/32767+1只能产生32768个介于1和100000之间的数字,而不是整个范围。你是对的。。。我再也不能随意榨取美元了!<:考虑到马库斯的问题:……不那么专注,也不是那么随机,而是高效的抽样……刚刚发现舒夫是为我做的!相应地编辑了我的答案,尽管我没有删除前面的方法,因为我认为这是一个有用的练习。b来自哪里?我以前从未见过这种语法中的NR。。。你能解释一下b[a[i]]=a[i]}NR在b中的意思吗?请看我的更新答案和一些解释。我希望这是清楚的,不要犹豫,要求更多的澄清。我认为这种方法非常快速和简单。顺便说一句/bin/awk:参数列表太长会出现在我的文件中。我还没有想过。让我们换一种方式来做:将shuf列表作为一个文件。你可以在我更新的答案中找到它。为什么不干脆shuf-n$$wc-l<$FILENAME/10$FILENAME?这个b来自哪里?我以前从未见过这种语法中的NR。。。你能解释一下b[a[i]]=a[i]}NR在b中的意思吗?请看我的更新答案和一些解释。我希望这是清楚的,不要犹豫,要求更多的澄清。我认为这种方法非常快速和简单。顺便说一句/bin/awk:参数列表太长会出现在我的文件中。我还没有想过。让我们换一种方式来做:将shuf列表作为一个文件。你可以在我更新的答案中找到。为什么不干脆shuf-n$$wc-l<$FILENAME/10$FILENAME呢?谢谢。这就是我想在python中做的事情。但是python readline将\r视为新行字符。我不熟悉perl,所以让我检查一下当一行中包含\r字符时它是否有效。谢谢。这就是我想在python中做的事情。但是python readline将\r视为新行字符。我不熟悉perl,所以让我检查一下当一行中包含\r字符时它是否有效。