【问题标题】:how to get unique values set from a repeating values list如何从重复值列表中获取唯一值集
【发布时间】:2011-09-23 22:07:19
【问题描述】:

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

两列中的值都是重复的。我需要找到 column-A 中的每个唯一值,我需要找到一组 column-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' 获取 column-A 的唯一值列表

标签: python perl parsing


【解决方案1】:

Perl 'one-liner' 旨在/扩展,以便所有内容都适合窗口:

$ perl -F -lane '

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

      for my $columnA ( keys %hash ) {

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

如果我看到原始海报的部分一致尝试,将会进行解释。

【讨论】:

    【解决方案2】:

    我会使用 Python 字典,其中字典键是 A 列值,字典值是 Python 的内置 Set type 保存 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
    

    【讨论】:

    • 使用collections.defaultdict,把d = {}改成d = defaultdict(set),然后你就可以摆脱冗长的try-except了,只需要d[lower(A)].add(B),defaultdict就会负责初始化首次见到的钥匙的新套装。
    【解决方案3】:

    您可以使用这个简单的多图:

    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 想要集合,而不是列表。
    【解决方案4】:

    简单的 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
    

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

    【讨论】:

    • ++ 用于注意案件,尽管我认为这是原始海报的错字
    【解决方案5】:
    
    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
    • 在这个python代码中,colum-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 不完全确定您的意思。 @nmichaels 感谢您的提示
    【解决方案6】:
    
    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";
    
    }
    

    【讨论】:

      【解决方案7】:

      使用 Perl oneliner:

      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$&amp;/e;,或者如果您可以将您的密钥统一为小写或大写,则使用 $F[0]=lc$F[0];$F[0]=uc$F[0];

      【讨论】:

        猜你喜欢
        • 2013-06-12
        • 2012-10-05
        • 2021-06-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-06-22
        相关资源
        最近更新 更多