繁体   English   中英

使用awk或perl按键对文件进行排序,就像联接一样,而无需预先排序

[英]Sort file by key with awk or perl like a join without presorting

我想加入两个制表符分隔的文件,但是它们的顺序不同。 我知道用awk可行,但是我不知道怎么做。 这是等效的玩具python代码(如果没有疯狂的解决方法,python对于此任务而言内存太低):

import pandas as pd
from random import shuffle

a = ['bar','qux','baz','foo','spam']
df = pd.DataFrame({'nam':a,'asc':[1,2,3,4,5],'desc':[5,4,3,2,1]})

shuffle(a)
print(a)

dex = pd.DataFrame({'dex' : a})
df_b = pd.DataFrame({'VAL1' :[0,1,2,3,4,5,6]})

pd.merge(dex, df,left_on='dex',right_on='nam')[['asc','desc','nam']]

我有两个文件:对于文件1,第2列保存每行的标识符,我不需要5列,然后大约有300万列数据。

对于文件2,共有12列,第二列包含具有不同顺序的相同标识符以及其他ID。

我想对文件一进行排序,使其具有与文件二相同的标识符和顺序,而其他列则进行了适当的重新排列。

文件一可能是多个千兆字节。

使用awk和/或其他GNU工具是否更容易,还是应该使用perl?

如果file1的大小约为GB,并且有300万列数据,则行数很少(不超过200行)。 虽然您无法将所有行本身都加载到内存中,但是可以轻松加载它们的所有位置

use feature qw( say );

use Fcntl qw( SEEK_SET );

open(my $fh1, '<', $qfn1) or die("Can't open \"$qfn1\": $!\n");
open(my $fh2, '<', $qfn2) or die("Can't open \"$qfn2\": $!\n");

my %offsets;
while (1) {
   my $offset = tell($fh1);
   my $row1 = <$fh1>;
   last if !defined($row1);

   chomp($row1);
   my @fields1 = split(/\t/, $row1);
   my $key = $fields1[1];
   $offsets{$key} = $offset;
}

while (my $row2 = <$fh2>) {
   chomp($row2);
   my @fields2 = split(/\t/, $row2);
   my $key = $fields2[1];
   my $offset = $offsets{$key};
   if (!defined($offset)) {
      warn("Key $key not found.\n");
      next;
   }

   seek($fh1, $offset, SEEK_SET);
   my $row1 = <$fh1>;
   chomp($row1);
   my @fields1 = split(/\t/, $row1);

   say join "\t", @fields2, @fields1[6..$#fields1];
}

这种方法也可以在Python中采用。

注意:如果订单是更灵活的(也就是说,如果你真行与作为记录在有序进行排序的输出存在一个更简单的解决file1 )。 假设file2很容易放入内存。

重要的是不要进行不必要的拆分 如果您有足够的内存,则将较小的文件放入哈希中,然后再读取第二个文件应该可以工作。

考虑以下示例(请注意,此脚本的运行时间包括创建示例数据所需的时间):

#!/usr/bin/env perl

use strict;
use warnings;

# This is a string containing 10 lines corresponding to your "file one"
# Second column has the record ID
# Normally, you'd be reading this from a file

my $big_file = join "\n",
    map join("\t", 'x', $_, ('x') x 3_000_000),
    1 .. 10
;

# This is a string containing 10 lines corresponding to your "file two"
# Second column has the record ID

my $small_file = join "\n",
    map join("\t", 'y', $_, ('y') x 10),
    1 .. 10
;

# You would normally pass file names as arguments

join_with_big_file(
    \$small_file,
    \$big_file,
);

sub join_with_big_file {
    my $small_records = load_small_file(shift);
    my $big_file = shift;

    open my $fh, '<', $big_file
        or die "Cannot open '$big_file': $!";

    while (my $line = <$fh>) {
        chomp $line;
        my ($first, $id, $rest) = split /\t/, $line, 3;
        print join("\t", $first, $id, $rest, $small_records->{$id}), "\n";
    }

    return;
}

sub load_small_file {
    my $file = shift;
    my %records;

    open my $fh, '<', $file
        or die "Cannot open '$file' for reading: $!";

    while (my $line = <$fh>) {
        # limit the split
        my ($first, $id, $rest) = split /\t/, $line, 3;
        # I drop the id field here so it is not duplicated in the joined
        # file. If that is not a problem, $records{$id} = $line
        # would be better.
        $records{$id} = join("\t", $first, $rest);
    }

    return \%records;
}

300万列数据,是吗? 听起来您正在做一些NLP工作。

假设这是真的,并且您的矩阵是稀疏的,则python可以很好地处理它(只是不能使用pandas )。 scipy.sparse 例:

from scipy.sparse import dok_matrix

A = dok_matrix((10,10))
A[1,1] = 1

B = dok_matrix((10,10))
B[2,2] = 2

print A+B

DOK代表“密钥字典”,通常用于构建稀疏矩阵,然后根据使用情况通常将其转换为CSR等。 请参阅可用的稀疏矩阵类型

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM