如何从重复值列表中获取唯一值集合

3

我需要解析一个大型日志文件(平面文件),其中包含两列值(列A,列B)。

两个列中的值都是重复的。 我需要为每个唯一的列A值找到一组列B值。

这可以使用Unix shell命令完成,还是需要编写任何Perl或Python脚本? 有哪些方法可以做到这一点?

示例:

xxxA 2
xxxA 1
xxxB 2
XXXC 3
XXXA 3
xxxD 4

输出:

xxxA - 2,1,3
xxxB - 2
xxxC - 3
xxxD - 4

我尝试使用shell命令“cut”、“sort”、“uniq”获取列A的唯一值列表。 - Mariselvam
7个回答

5

将Perl的“一行代码”扩展到适合窗口的大小:

$ perl -F -lane '

      $hash{ $F[0] }{ $F[1] }++;
  } END {

      for my $columnA ( keys %hash ) {

          print $columnA, " - ", join( ",", keys %$hash{$columnA} ), "\n";
      }
  '

如果原帖作者有刻意尝试,我会提供解释。

3
我会使用Python字典,其中字典键是A列的值,字典值是Python内置的Set类型,保存B列的值。
def parse_the_file():
    lower = str.lower
    split = str.split
    with open('f.txt') as f:
        d = {}
        lines = f.read().split('\n')
        for A,B in [split(l) for l in lines]:
            try:
                d[lower(A)].add(B)
            except KeyError:
                d[lower(A)] = set(B)

        for a in d:
            print "%s - %s" % (a,",".join(list(d[a])))

if __name__ == "__main__":
    parse_the_file()

使用字典的优点是您将拥有每个A列值的单个字典键。使用集合的优点是您将拥有唯一的B列值集合。
效率说明:
- 使用try-catch比使用if / else语句检查初始情况更有效。 - 在循环之外计算和赋值str函数比在循环内部简单地使用它们更有效。 - 根据文件中新A值与旧A值再次出现的比例,您可以在try catch语句之前使用 a = lower(A) 。 - 我使用了一个函数,因为在Python中访问局部变量比访问全局变量更有效。 - 这些性能提示中的一些来自here
对上面的代码示例进行测试,得到以下结果:
xxxd - 4
xxxa - 1,3,2
xxxb - 2
xxxc - 3

2
使用collections.defaultdict,将“d = {}”更改为“d = defaultdict(set)”,然后您可以摆脱冗长的try-except,只需执行“d [lower(A)] .add(B)”,defaultdict将负责初始化新集合以用于第一次查看的键。 - PaulMcG

1

您可以使用这个简单的 multimap:

class MultiMap(object):
    values = {}

    def __getitem__(self, index):
        return self.values[index]
    def __setitem__(self, index, value):
        if not self.values.has_key(index):
            self.values[index] = []
        self.values[index].append(value)
    def __repr__(self):
        return repr(self.values)

看它如何运作:http://codepad.org/xOOrlbnf


-1:学习标准库,collections.defaultdict与您的MultiMap相同。另外:has_key早已被弃用,推荐使用key in dict,在您的情况下是if not index in self.values:;而且OP想要集合,不是列表。 - PaulMcG

1

简单的 Perl 版本:

#!/usr/bin/perl

use strict;
use warnings;

my (%v, @row);

foreach (<DATA>) {
        chomp;
        $_ = lc($_);
        @row = split(/\s+/, $_);
        push( @{ $v{$row[0]} }, $row[1]);
} 

foreach (sort keys %v) {
        print "$_ - ", join( ", ", @{ $v{$_} } ), "\n";
}

__DATA__
xxxA 2
xxxA 1
xxxB 2
XXXC 3
XXXA 3
xxxD 4

没有关注变量名称。从例子中我看到它们不区分大小写。


++ 用于注意大小写,尽管我认为这是原帖的一个打字错误。 - plusplus

0

使用 Perl 单行命令:

perl -lane'$F[0]=~s/.../lc$&/e;exists$s{$F[0]}and$s{$F[0]}.=",$F[1]"or push@v,$F[0]and$s{$F[0]}=$F[1]}{print"$_ $s{$_}"for@v'

如果您的键区分大小写(在测试数据中不是这样),则可以删除$F [0] =〜s / ... / lc $&/ e; ,或者使用 $ F [0] = lc $ F [0]; 或 $ F [0] = uc $ F [0]; ,如果您可以将键统一为小写或大写。

0

f = """xxxA 2
xxxA 1
xxxB 2
XXXC 3
XXXA 3
xxxD 4"""


d = {}

for line in f.split("\n"):
    key, val = line.lower().split()
    try:
        d[key].append(val)        
    except KeyError:
        d[key] = [val]


print d

Python


你可以通过使用d.setdefault(key, []).append(val)来避免使用try/except。 - nmichaels
在这段Python代码中,B列的值重复如下:{'xxxd': ['4'], 'xxxa': ['2', '1', '5', '2', '1'], 'xxxb': ['2', '1', '4', '2'], 'xxxc': ['3']}。实际上它应该像这样:{'xxxd': ['4'], 'xxxa': ['2', '1', '5'], 'xxxb': ['2', '1', '4'], 'xxxc': ['3']}。 - Mariselvam
@Mariselvam 不太确定你的意思。@nmichaels 感谢你的提示。 - Asterisk

0

while() {

($key, $value) = split / /, $_;

$hash{lc($key)} = 1;

push(@array, "$key$value");

}

foreach $key (sort keys %hash) {

@arr = (grep /$key/i, @array);

chomp(@arr);

$val = join (", ", @arr);

$val =~ s#$key##gi; 

print "$key\t$val\n";

}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接