2

5000 万行のファイルがあり、そこから 1000 行をランダムに抽出する必要があります。

まず、1000 個の乱数を作成します。それから私は使用します

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

それは本当に遅いです; 1 行の出力には少なくとも 5 ~ 6 秒かかります。

したがって、印刷固有の回線速度を最適化する必要があると思います。

特定の行を印刷する方法はたくさんあります。

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

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

head -$line file | tail -1

それはすべて遅いです...特定の行を印刷するのに約5〜6秒かかります。

シェルで特定の行を印刷する他の方法はありますか?または、python、perlはシェルよりも高速ですか? または、この問題を解決する私の方法が間違っていますか?

- - - - - - - - - - - - - - - - - - - - スプリット - - - - - --------------------------------------

1000回の乱数を反復し、毎回シェルを1回使用すると、1000回のio操作が生成される可能性があります。最初に乱数を保存し、ファイルを1回反復するために配列を使用する必要があるかもしれません。

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 週間以上) 変更できない場合は、前処理が必要になります。問題の解決策は次のとおりです。

  1. 同じ行でファイルをいくつかの小さなサイズに分割します
  2. 各ファイルを 1 つのファイルに貼り付けます。その後、リンク 1 には 1 の情報が含まれます 1+n 1+2n の情報
  3. ラインを計算するためのラップシェルが必要になります。

ご存知のように、上記は単なる方法です。

于 2013-04-16T06:29:50.773 に答える
0

使用するツールに関係なく、これらの行を見つけるには固有のコストがかかります。本質的に、改行記号を見つけて数えながら、その大きなファイルを毎回トラバースする必要があります。

私が見ることができる2つの解決策があります:

  1. ファイル内の行オフセットを 1 回のパスで事前計算し、それを使用lseekして出力を見つけます。スペースを節約するために、100 行または 1000 行ごとにオフセットを格納できます。

  2. 事前に行番号のリスト全体を生成し、ファイルを 1 回で行を収集します。次に、それらを印刷します。(行の順序をランダムにしたい場合は、そのまま印刷することはできません)。

これらのいずれかをシェルで行うのは難しいでしょう。シェルのみのソリューションについては、devnull の提案を試してくださいshuf。ただし、1 の代わりに 1000 を使用する必要があります。

shuf -n 1000 file
于 2013-04-16T06:14:21.050 に答える