2

我有一个包含 5000 万行的文件,我必须从中随机抽取 1000 行。

首先,我创建 1000 个随机数;然后我用

sed -n "$random{p;q}" file  

真的很慢;一条线输出至少要5-6秒。

所以我认为我应该优化打印特定的线速度。

我们有很多方法可以打印特定的行:

sed -n "$line{p;q}" file

awk "NR==$line{print}" file

head -$line file | tail -1

这一切都很慢......打印特定行大约需要 5-6 秒。

shell中是否有其他方法可以打印特定行?或者python,perl可以比shell更快?还是我解决这个问题的方法不对?

- - - - - - - - - - - - - - - - - - - - 分裂 - - - - - -------------------------------------

迭代1000个随机数,每次使用shell一次,可能会产生1000次io操作。也许我应该先用一个数组来保存随机数,然后迭代一次文件。

random_array=()

awk '{if ( NR in $random_array ) print;}' file

好吧,我会以这种方式测试并稍后粘贴结果

4

4 回答 4

2

为了避免读取整个文件,您可以获取文件的大小,然后生成 0 和该数字之间的 1000 个偏移量的列表。这些通常是位于一行中间的位置,但您可以通读到下一个换行符,然后阅读并打印下一行。但是,这会对文件的第一行产生偏差。如果您对平均线长有一个猜测,您可以从生成的偏移量中减去该数字(任何负面结果都意味着从偏移量 0 读取和打印。)

这是概念的快速证明。出于说明目的,我假设平均行长约为 75 个字符。这也会影响公平性(选择长线之后的线的可能性更高)。最后一行的处理也不公平;如果它短于 75 个字符,则永远无法选择它(!)——您可以尝试通过根据您实际阅读的行计算实际平均行长度来解决这个问题,但我将其作为练习,以保持这个例子相当紧凑。

#!/usr/bin/perl

use strict;
use warnings;

use Fcntl (qw(SEEK_SET SEEK_CUR SEEK_END));

my $n = (defined @ARGV ? shift @ARGV : '--help');
die "Syntax: $0 number file\n" unless @ARGV == 1 and $n =~ m/^[0-9]+$/;

open (F, "<", $ARGV[0]) or die "$0: Could not open $ARGV[0]: $!\n";

seek (F, 0, SEEK_END) or die "$0: Could not SEEK_END $ARGV[0]: $!\n";
my $max = tell(F);

my %seen;
for (my $i=0; $i < $n; ++$i)
{
    my $offset = int(rand($max))-75;
    my $first = 0;
    if ($offset < 0)
    {
        $offset = 0;
        $first = 1;
    }
    seek (F, $offset, SEEK_SET)
        or die "$0: Could not SEEK_SET $ARGV[0]: $!\n";
    <F> unless $first;
    redo if eof (F);   # Cheap trick, just retry if at eof
    redo if $seen{tell(F)}++;
    print scalar(<F>);
}

我添加了代码以避免重复;这是%seen哈希。

于 2013-04-16T16:04:10.603 回答
1

按照文件中的行顺序,没有内存中的所有行:

awk '
  NR==FNR { next }
  FNR==1{
    srand;
    n=NR-1
    for(i=1; i<=1000; i++) {
      line=0
      while(!line || line in A) line=int(rand*n)+1
      A[line]
    }
  } 
  FNR in A
' infile infile
于 2013-04-16T05:43:59.560 回答
0

无论您使用哪种工具,查找这些线路都存在固有成本。本质上,您每次都需要遍历该大文件,查找并计算换行符。

我可以看到两种解决方案:

  1. 一次性预先计算文件中的行偏移,然后用于lseek查找打印它们。您可以存储每 100 或 1000 行偏移以节省空间。

  2. 预先生成完整的行号列表,并一次性收集文件中的行。然后打印它们。(如果您希望行的顺序是随机的,则不能随时打印)。

这些中的任何一个都很难在 shell 中完成。对于仅外壳的解决方案,请尝试 devnull 的建议,shuf. 但是你想要使用 1000 而不是 1:

shuf -n 1000 file
于 2013-04-16T06:14:21.050 回答
0

如果您只想从较大规模的数据文件中获取特定行,则成本将根据您的要求增加。如果您的文件在一段时间(一周或更长时间)内是不可变的,则需要进行预处理,您的问题有一个解决方案:

  1. 使用同一行将文件拆分为较小的大小
  2. 将每个文件粘贴到一个文件中;之后,链接 1 将包含 1 1+n 1+2n 信息的信息
  3. 需要一个包装壳来计算线。

如您所知,以上只是一种方法。

于 2013-04-16T06:29:50.773 回答