我想随机输出文件总行的 10% 行。例如,文件 a 有 1,000,000 行,那么我想从文件中随机输出 100,000 行(100,000 是 1,000,000 的 10%)。
假设文件很小,这很容易做到:
randomLine=`wc -l a | awk '{printf("%dn",($1/10))}'`
sort -R a | head -n $randomLine
但是使用 sort -R 非常慢。它将执行专用的随机计算。我的文件有 10,000,000 行。排序需要太多时间。有没有办法存档一个不那么专用且不那么随机但有效的采样?
编辑想法:
- 每十行采样一行是可以接受的。但是我不知道如何使用shell脚本来做到这一点。
逐行阅读,如果
echo $RANDOM%100 | bc
大于20输出线(使用大于10的数字保证得到不少于10%的线),一旦输出10%线则停止。但是我不知道如何使用shell脚本逐行阅读。
编辑说明
我想使用 shell 脚本的原因是我的文件包含 \r 字符。文件中的新行字符应该是,但Python和Java中的readline()函数将\r和视为换行符,这不符合我的需要。
让我们创建一个从 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
起色
正如普伦德拉在评论中建议的那样:
shuf -n $(( $(wc -l < $FILENAME) / 10 )) $FILENAME
我认为这是最好的方法:
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 之间的任何数字,而是 1 到 100000 之间的 32768 个可能数字之一,所以这是一种投影......
所以:
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
我有这个脚本,可以给你大致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
只需使用文件作为输入运行此 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
数字.txt包含数字 1 到 1,000,000,每行一个。在多次运行中,文件编号.txt通常包含大约 100,200 个项目,即 10.02%。
如果 awk 认为是一行的内容有问题,您可以随时更改记录分隔符。这是RS = "n";
但这应该是Linux机器上的默认设置。
这是编辑想法 1的一种方法。 在 bash 中:
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
字符在不修改的情况下传递,如编辑要求中所述。