重新排列来自多个数据文件的数据

时间:2013-01-23 00:02:48

标签: python perl bash awk fortran

我有40,000个数据文件。每个文件在单列中包含1445行浮点数。现在我需要以不同的顺序重新排列数据。

需要收集每个数据文件中的第一个数字并将其转储到新文件中(假设为abc1.dat)。此特定文件(abc1.dat)将包含40,000个数字。

每个数据文件中的第二个数字需要被提取并转储到另一个新文件中(比方说abc2.dat)。这个新文件也将包含40,000个数字。但只有每个数据文件中的第二个数字。

在这个操作结束时我应该有1445个文件(abc1.dat,abc2.dat,... abc40000.dat),每个文件包含40,000个数据。

如何实现这一目标? (使用Linux Ubuntu 11.10 - 64位)

感谢任何帮助。提前谢谢。

8 个答案:

答案 0 :(得分: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";
    }
}

答案 1 :(得分: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的数据,这对于具有8GB RAM的64位机器来说可能没问题......),你可以这样做:< / p>

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中的infile * 250行。

如果批处理解决方案太慢,则在每个文件的每个批处理结束时,隐藏infile.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()

答案 2 :(得分:2)

你应该可以逃脱这样的单线:

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

它将打开一个新的输出文件,用于附加输入文件的每一行。输出文件将根据输入文件的当前行号命名。最后,我们需要显式关闭ARGV文件句柄以重置行号变量$.

您可以使用glob控制输入文件的顺序,或者如果您愿意,可以使用perl控制输入文件的顺序。我选择了泛型glob,因为你没有指定这些行应按特定顺序排列。

效率方面,我不认为为每一行打开一个新文件会非常耗时,因为perl在文件操作上相当快。

请注意,您不需要关闭输出文件句柄,因为它在超出范围时会自动关闭。另请注意,它不会关心您的文件大小。

答案 3 :(得分:2)

击:

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

假设所有文件都有1445行,写入outputprefix0000,outputprefix0001,... outputprefix1444。

有点慢,但它有效:)

答案 4 :(得分:1)

创建文件后,运行大约需要4分钟,笔记本电脑上使用3.6GB的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])

我将所有内容都保留为字符串,以避免反序列化然后重新序列化浮点数时的精度错误。


您是否需要更快,更少资源的东西才能定期运行,或者这是一次性转换?

答案 5 :(得分:1)

为了完整起见,因为[fortran]标签,Fortran中的一个迟来的例子。它逐个打开文件并将所有数据存储在内存中。

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

注意:它可能会很慢。

答案 6 :(得分:0)

在awk中,它非常简单:

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

我不确定awk是否能够处理40,000个打开的文件句柄。

答案 7 :(得分:0)

以下适用于solaris。

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

你无论如何都可以:

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

用于引用所有40k文件