从gz文件中获取每列的唯一值

时间:2017-07-26 10:23:30

标签: python bash shell

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

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,

并且想要每列中的所有唯一值。

3 个答案:

答案 0 :(得分:1)

使用gunzipped文件,您可以:

object sumObject = dtTable.Compute("Sum(Convert(" + col_Name + ", 'System.Int32')","");

将字段分隔符设置为,然后对于文件中的每个字段,构造一个通过uniq管道的剪切命令,最后通过sh管道整个awk响应。使用cut,uniq和sh会减慢速度,可能会有更有效的方法,但值得一试。

答案 1 :(得分: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}([^|]*)'。该表达式将跳过三列,第四列可以作为组1读取。如果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)

答案 2 :(得分: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 Vernierhis answer末尾提出的建议。请upvote这个答案在这里早于我的...

如果您想限制唯一值的数量,可以使用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'}]

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