2017-07-26 58 views
0

我有一个gz文件,我想从文件中提取每列的唯一值,字段分隔符是|,我尝试使用python如下。从gz文件获取每列的唯一值

import sys,os,csv,gzip 
from sets import Set 
ig = 0 
max_d = 1 
with gzip.open("fundamentals.20170724.gz","rb") as f: 
    reader = csv.reader(f,delimiter="|") 
    for i in range(0,400): 
     unique = Set() 
     print "Unique_value for column "+str(i+1) 
     flag = 0 
     for line in reader: 
      try: 
       unique.add(line[i]) 
       max_d +=1 
       if len(unique) >= 10: 
        print unique 
        flag = 1 
        break 
      except: 
       continue 
     if flag == 0: print unique 

我不觉得它对大文件有效,虽然它在某种程度上工作,但从bash的角度来看这个问题。

任何shell脚本解决方案?

例如我在我的文件中的数据作为

5C4423,COMP,ISIN,CA2372051094,2016-04-19, 
41C528,COMP,ISIN,US2333774071,2000-01-01, 
B62545,COMP,ISIN,NL0000344265,2000-01-01,2007-05-11 
9E7F41,COMP,ISIN,CA39260W1023,2013-02-13,2013-08-09 
129DC8,COMP,ISIN,US37253A1034,2012-09-07, 
4DE8CD,COMP,ISIN,QA000A0NCQB1,2008-03-06, 

,并希望从每列中的所有唯一值。

+0

您解析列表中每​​列的所有值,每列一列,并使用命令集(your_list),您将获得每个列的唯一值 –

+1

没有示例输入和所需的相应输出,很难猜猜你想要什么...请提供一个[最小化,完整和可验证的示例](https://stackoverflow.com/help/mcve),让那些试图帮助你的人更容易工作。 –

+0

你为什么从'套装进口套装'?这个模块是古老的,自从Python 2.4以来,Python已经有了一个内置的集合类型了。你使用的是什么Python版本?另外,请不要使用'except'命令,而是使用一个命名的异常,否则你可以捕获你不想捕获的东西。 OTOH,我很想知道你希望在那里尝试......除了......块之外。 –

回答

1

随着gunzip解压文件,你可以这样做:

awk -F, 'END { for (i=1;i<=NF;i++) { print "cut -d\",\" -f "i" filename | uniq" } }' filename | sh 

设置字段分隔符,然后对文件中的每个领域,构建通过uniq的和剪切命令管道终于管贯穿整个AWK响应SH。使用cut,uniq和sh会减慢速度,这可能是一种更有效的方式,但值得一试。

+0

这将打印所有,而不是唯一的,顺便说一句,谢谢你的尝试 – ggupta

+0

由于值作为索引放在数组中,打印索引将只打印唯一条目。 –

+0

尝试使用数据,如问题中的示例,给出所有的,不是唯一的 – ggupta

0

一个shell构建的管道确实可以更快地完成这项工作,尽管内存效率可能会更低。主要原因是两个:并行和本地代码。首先,由于我们对任务的描述很少,我必须阅读Python代码并找出它的作用。

from sets import Set是一条奇数行; sets是标准库的一部分,我不知道你的sets模块包含什么。我不得不猜测它是标准集合类型的另一个名称,或者至少是同一概念的效率较低的变体。

gzip.open让脚本读取gzip文件。我们可以用zcat过程替换它。

csv.reader读取字符分隔值,在这种情况下,拆分'|'。在代码内部更深入,我们发现只有一列(line[i])被读取,所以我们可以用cutawk替换它...直到i更改。 awk也可以处理这种情况,但它有点棘手。

最棘手的部分是结束逻辑。每当在一列中找到10个唯一值时,程序就会输出这些值并切换到下一列。顺便说一句,Python's for has an else clause specifically for this case,所以你不需要一个flag变量。

该代码的一个奇特的部分是如何捕捉内部数据处理块的所有异常。为什么是这样?这里基本上只有两个例外来源:首先,如果没有那么多列,索引可能会失败。其次,未知的Set类型可能会抛出异常;标准set类型不会。

因此,对函数的分析是:以对角方式(因为文件永远不会重绕,并且列不会并行处理),从每列中收集唯一值直到找到10个为止,然后将其打印出来。这意味着,例如,如果第一列有少于十个唯一项目,则不会为其他列打印任何内容。我不确定这是你想要的逻辑。

有了这样复杂的逻辑,Python的设置功能实际上是一个不错的选择;如果我们可以更容易地分割数据,那么uniq可能会更好。抛出我们的是程序如何从列到列移动,并且只需要特定数量的值。

因此,Python程序中的两个大时间浪费在与所有解析相同的线程中解压缩,并在我们只需要解析所有列时将其解压到所有列中。前者可以使用thread来解决,后者可能最好使用regular expression来完成,例如r'^(?:[^|]*\|){3}([^|]*)'。该表达式会跳过三列,第四个可以被读为第一组。如果CSV引用了某列中的分隔符,它会变得更加复杂。我们可以在一个单独的线程中自行解析行,但这并不能解决许多不需要的字符串分配的问题。

请注意,如果您真正想要的是从文件的开头处理所有列,问题实际上会变得相当不同。我也不知道为什么你专门处理400列,而不管现有的数量。如果我们去掉这两个限制,逻辑会更喜欢:

firstline=next(reader) 
sets = [{column} for column in firstline] 
for line in reader: 
    for column,columnset in zip(line,sets): 
     columnset.add(column) 
+1

显然'sets'模块在库中;但这是一个历史文物,并且比使用默认设置类型效率低。它在2.3中引入,在2.6中被弃用,并且我正在阅读更新的文档。 –

0

这是基于你的想法纯粹的Python版本:

from io import StringIO 
from csv import reader 

txt = '''5C4423,COMP,ISIN,CA2372051094,2016-04-19, 
41C528,COMP,ISIN,US2333774071,2000-01-01, 
B62545,COMP,ISIN,NL0000344265,2000-01-01,2007-05-11 
9E7F41,COMP,ISIN,CA39260W1023,2013-02-13,2013-08-09 
129DC8,COMP,ISIN,US37253A1034,2012-09-07, 
4DE8CD,COMP,ISIN,QA000A0NCQB1,2008-03-06,''' 


with StringIO(txt) as file: 
    rows = reader(file) 
    first_row = next(rows) 
    unique = [{item} for item in first_row] 
    for row in rows: 
     for item, s in zip(row, unique): 
      s.add(item) 

这将产生对您输入:

[{'129DC8', '41C528', '4DE8CD', '5C4423', '9E7F41', 'B62545'}, 
{'COMP'}, 
{'ISIN'}, 
{'CA2372051094', 
    'CA39260W1023', 
    'NL0000344265', 
    'QA000A0NCQB1', 
    'US2333774071', 
    'US37253A1034'}, 
{'2000-01-01', '2008-03-06', '2012-09-07', '2013-02-13', '2016-04-19'}, 
{'', '2007-05-11', '2013-08-09'}] 

哎呀,现在我发布了我的答案,我看到,这正是Yann Vernier建议在his answer的末尾。请给予好评这个答案这是在这里比我更早的方式...


,如果你想限制唯一值的数量,你可以使用一个deque数据结构:

from io import StringIO 
from csv import reader 

MAX_LEN = 3 

with StringIO(txt) as file: 
    rows = reader(file) 
    first_row = next(rows) 
    unique = [{item} for item in first_row] 
    for row in rows: 
     for item, s in zip(row, unique): 
      if len(s) < MAX_LEN: 
       s.add(item) 

print(unique) 

与结果如下:

[{'41C528', '5C4423', 'B62545'}, 
{'COMP'}, 
{'ISIN'}, 
{'CA2372051094', 'NL0000344265', 'US2333774071'}, 
{'2000-01-01', '2013-02-13', '2016-04-19'}, 
{'', '2007-05-11', '2013-08-09'}] 

这样,如果某列仅包含唯一值,则可以节省一些内存。

+0

它可以用于大型数据文件吗? – ggupta

+0

对csv文件的迭代是惰性的;内存中有一行在任何给定的时间;消耗内存的是唯一值列表。但是对于所有实现来说这是一个问题(对文件迭代一次)。所以我的猜测是:是的,这应该很好。为什么不试一试大文件呢? –

+0

好吧,它没有结束条件,所以任何具有一致唯一值的列将被全部加载。原来的代码不会超过10个。这个练习应该是制作一个有限尺寸的集合;也许只是将add调用包装在一个'if len(s)<10'中。 –