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

时间:2014-02-13 12:42:05

标签: linux algorithm bash sorting random

我想输出一个文件总行的随机10%行。例如,文件a有1,000,000行,然后我想从文件中输出随机的100,000行(100,000是1,000,000的10%)。

有一个很容易做的事情,假设文件很小:

randomLine=`wc -l a | awk '{printf("%d\n",($1/10))}'`
sort -R a | head -n $randomLine

但是使用sort -R非常慢。它将执行专门的随机计算。我的文件有10,000,000行。排序需要太多时间。无论如何归档一个不那么专注但不那么随机但有效的采样?

修改提示

  1. 每十行采样一行是可以接受的。但是我不知道如何用shell脚本来做这件事。
  2. 逐行阅读

    echo $RANDOM%100 | bc
    
  3. 大于20比输出线(使用大于10的数字以确保得到不小于10%的线)并且一旦输出10%线然后停止。但我不知道如何使用shell脚本逐行阅读。

    修改说明

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

5 个答案:

答案 0 :(得分:2)

让我们创建一个从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

解释

  • FNR==NR {a[$1]; next}循环显示shuf结果并将其存储在a[]数组中。
  • {if (FNR in a) print}如果在数组file中找到第二个参数(a[])的行号,请将其打印出来。

Y = 10,X = 2

的样品
$ cat a
1 hello
2 i am
3 fe
4 do
5 rqui
6 and
7 this
8 is 
9 sample
10 text

$ awk 'FNR==NR {a[$1]; next} {if (FNR in a) print}' <(shuf -i 1-10 -n2) a
2 i am
9 sample

$ awk 'FNR==NR {a[$1]; next} {if (FNR in a) print}' <(shuf -i 1-10 -n2) a
4 do
6 and

改进

正如plundra建议in comments

shuf -n $(( $(wc -l < $FILENAME) / 10 )) $FILENAME

答案 1 :(得分:1)

我认为这是最好的方法:

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

shuf -n $lines_wanted $file

另一个创意解决方案:

echo $RANDOM生成0到32767之间的随机数

然后,你可以这样做:

echo $(($RANDOM*100000/32767+1))

..要获得1到100000之间的随机数(如nwellnhof所示,在下面的评论中指出,它不是任何数字从1到100000,而是在32768之间可能的数字之一1和100000,所以它是一种投影......)

所以:

file=your file here
lines_in_file=`wc -l $file | awk {'print $1'}`
lines_wanted=$(($lines_in_file/10))
for i in `seq 1 $lines_wanted`
 do line_chosen=$(($RANDOM*${lines_in_file}/32767+1))
sed "${line_chosen}q;d" $file
done

答案 2 :(得分:0)

我有这个脚本,它会给你大约1/x行。

#!/usr/bin/perl -w

use strict;

my $ratio = shift;

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

对于足够大的$ratio,假设rand的输出均匀分布。

假设您调用此random_select_ratio.pl,请按此方式运行以获得10%的行:

random_select_ratio.pl 10 my_file

cat my_file | random_select_ratio.pl 10

答案 3 :(得分:0)

只需以文件作为输入运行此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到1,000,000,每行一个。在多次运行中,文件nums.txt通常包含大约100,200个项目,达到10.02%。

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

答案 4 :(得分:0)

以下是修改主题1。的一种方法:

while readarray -n10 a; do
    [ ${#a[@]} = 0 ] && break
    printf "%s" "${a[${RANDOM: -1:1}]}"
done < largefile.txt

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

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

使用readarray / printf组合应确保\r字符未经修改即通过,如编辑后的要求。