4

40,000 個のデータ ファイルがあります。各ファイルには、1 つの列に 1445 行の浮動小数点数が含まれています。ここで、データを別の順序で並べ替える必要があります。

各データ ファイルの最初の数値を収集し、新しいファイル (abc1.dat としましょう) にダンプする必要があります。この特定のファイル (abc1.dat) には、40,000 個の数字が含まれます。

そして、各データ ファイルから 2 番目の数値を抽出し、別の新しいファイル (abc2.dat としましょう) にダンプする必要があります。この新しいファイルにも 40,000 の数字が含まれます。ただし、各データ ファイルの秒数のみ。

この操作の最後に、1445 個のファイル (abc1.dat、abc2.dat、...abc40000.dat) があり、それぞれに 40,000 個のデータが含まれていると想定しました。

これはどのように達成できますか?(Linux Ubuntu 11.10 - 64 ビットを使用)

どんな助けにも感謝します。よろしくお願いします。

4

8 に答える 8

5

40,000 * 1445 はそれほど多くないので、メモリに収まるはずです。したがって、Perl (未テスト) では:

#!/usr/bin/perl
use strict;
use warnings;

my @nums;
# Reading:
for my $file (0 .. 40_000) {
    open my $IN, '<', "file-$file" or die $!;
    while (<$IN>) {
        chomp;
        $nums[$file][$.-1] = $_;
    }
}

# Writing:
for my $line (0 .. 1444) {
    open my $OUT, '>', "abc$line.dat" or die $!;
    for my $file (0 .. 40_000) {
        print $OUT $nums[$file][$line], "\n";
    }
}
于 2013-01-23T00:10:47.370 に答える
3

1445 個の出力ファイルすべてを一度に開くことができれば、これは非常に簡単です。

paths = ['abc{}.dat'.format(i) for i in range(1445)]
files = [open(path, 'w') for path in paths]
for inpath in ('input{}.dat'.format(i) for i in range(40000)):
    with infile as open(inpath, 'r') as infile:
        for linenum, line in enumerate(infile):
            files[linenum].write(line)
for f in files:
    f.close()

すべてをメモリに収めることができる場合 (これは約 0.5 ~ 5.0 GB のデータになるように思われますが、8 GB の RAM を搭載した 64 ビット マシンでは問題ないかもしれません…)、次の方法でそれを行うことができます。

data = [[] for _ in range(1445)]
for inpath in ('input{}.dat'.format(i) for i in range(40000)):
    with infile as open(inpath, 'r') as infile:
        for linenum, line in enumerate(infile):
            data[linenum].append(line)
for i, contents in enumerate(data):
    with open('abc{}.dat'.format(i), 'w') as outfile:
        outfile.write(''.join(contents)

これらのどちらも適切でない場合は、ある種のハイブリッドが必要になる場合があります。たとえば、一度に 250 個のファイルを実行できる場合は、6 つのバッチを実行しbatchnum、各 で *250 行以上スキップしますinfile

バッチ ソリューションが遅すぎる場合は、各ファイルの各バッチの最後に stashinfile.tell()を使用し、再びファイルに戻ったときに を使用infile.seek()してそこに戻ります。このようなもの:

seekpoints = [0 for _ in range(40000)]
for batch in range(6):
    start = batch * 250
    stop = min(start + 250, 1445)
    paths = ['abc{}.dat'.format(i) for i in range(start, stop)]
    files = [open(path, 'w') for path in paths]
    for infilenum, inpath in enumerate('input{}.dat'.format(i) for i in range(40000)):
        with infile as open(inpath, 'r') as infile:
            infile.seek(seekpoints[infilenum])
            for linenum, line in enumerate(infile):
                files[linenum].write(line)
            seekpoints[infilenum] = infile.tell()
    for f in files:
        f.close()
于 2013-01-23T00:16:48.187 に答える
2

次のようなワンライナーで逃げることができるはずです。

perl -nwe 'open my $fh, ">>", "abc${.}.dat" or die $!; 
           print $fh $_; close ARGV if eof;' input*.dat

入力ファイルの各行に追加するために、新しい出力ファイルを開きます。出力ファイルは、入力ファイルの現在の行番号に従って名前が付けられます。最後に、ARGV ファイル ハンドルを明示的に閉じて、行番号変数をリセットする必要があります$.

入力ファイルの順序は、グロブまたは必要に応じて perl で制御できます。行を特定の順序にする必要があることを指定しなかったため、一般的なグロブを選択しました。

効率的には、perl はファイル操作がかなり高速なので、行ごとに新しいファイルを開くのに過度に時​​間がかかるとは思いません。

スコープ外になると自動的に閉じられるため、出力ファイル ハンドルを閉じる必要がないことに注意してください。また、ファイルサイズは気にしないことに注意してください。

于 2013-01-23T00:38:56.603 に答える
2

バッシュ:

cat file1 file2 ... file40000 | split -n r/1445 -d - outputprefix

すべてのファイルが正確に 1445 行あるとすると、outputprefix0000、outputprefix0001、... outputprefix1444 に書き込みます。

少し遅いですが、動作します:)

于 2013-01-23T01:04:17.830 に答える
1

完全を期すために、Fortranの遅れた例である[fortran]タグのためです。ファイルを1つずつ開き、すべてのデータをメモリに保存します。

program copy
  implicit none

  character(1024) :: filename
  integer :: i, unit, infiles, outfiles
  parameter (infiles = 40000, outfiles = 1445)
  real :: data(infiles, outfiles)

  do i = 1, infiles
    write(filename, '("path/to/file", I0, ".dat")') i
    open(newunit = unit, file = filename, action = 'read')
    read(unit, *) data(i,:)
    close(unit)
  enddo

  do i = 1, outfiles
    write(filename, '("path/to/abc", I0, ".dat")') i
    open(newunit = unit, file = filename, action = 'write')
    write(unit, '(G0)') data(:,i)
    close(unit)
  enddo
end program

注:おそらくかなり遅くなります。

于 2013-01-23T16:24:43.580 に答える
1

ファイルが作成されると、実行に約 4 分かかり、ラップトップで 3.6 GB の RAM を使用しました。マシンに 8GB の RAM が搭載されている場合は、問題ありません。

#!/usr/bin/env python2.7

import random

NUMFILES = 40000
NUMLINES = 1445

# create test files
for i in range(1, NUMFILES + 1):
    with open('abc%s.dat' % i, 'w') as f:
        for j in range(NUMLINES):
            f.write('%f\n' % random.random())

data = []

# load all data into memory
for i in range(1, NUMFILES + 1):
    print i
    with open('abc%s.dat' % i) as f:
        lines = f.readlines()
        data.append(lines)

# write it back out
for j in range(len(data[0])):
    with open('new_abc%s.dat' % (j + 1), 'w') as f:
        for i in range(len(data)):
            f.write(data[i][j])

浮動小数点数をデシリアライズしてから再シリアライズする際の精度エラーを避けるために、すべてを文字列のままにしました。


定期的に実行できる高速でリソース消費の少ないものが必要ですか、それとも 1 回限りの変換ですか?

于 2013-01-23T00:33:17.553 に答える
0

awk では、非常に簡単です。

awk '{print >> "abc" FNR ".dat}' files*

ただし、awk が 40,000 の開いているファイル ハンドルを処理できるかどうかはわかりません。

于 2013-01-23T00:56:53.750 に答える
0

以下はsolarisで動作します。

nawk '{x="abc"FNR".txt";print $1>x}' file1 file2

とにかくできる:

nawk '{x="abc"FNR".txt";print $1>x}' file*

すべての 40k ファイルを参照するため

于 2013-01-23T06:03:34.767 に答える