将巨大的三列表转换为表的脚本

时间:2010-10-28 15:23:46

标签: python r algorithm scripting import-from-csv

我有一组数据(CSV文件),采用以下3列格式:

A, B, C
3277,4733,54.1 
3278,4741,51.0 
3278,4750,28.4 
3278,4768,36.0 
3278,4776,50.1 
3278,4784,51.4 
3279,4792,82.6 
3279,4806,78.2 
3279,4814,36.4 

我需要得到一个三向列联表:(对不起,这看起来不太好)

A /B     4733      4741      4750      4768      4776      4784      4792      4806      4814
3277   C 54.1                                                
3278                 51      28.4        36      50.1      51.4                  
3279                                                                 82.6      78.2      36.4

与excel“数据透视表”类似,OpenOffice数据导频,或R“表(x,y,z)”

问题是我的数据集是巨大的(总行数超过500,000,在A和B中有大约400个不同因素。(OOo,MSO和R限制阻止实现此目的)

我确信可以使用Python脚本来创建这样的表。 A和B都是数字(但可以视为字符串)。

有人处理过此事吗? (C或Java中的伪代码或代码也受到欢迎......但我更喜欢python,因为它实现起来更快:)

修改 几乎拥有它,感谢John Machin。以下Python脚本几乎提供了我正在寻找的东西,但是,在编写输出文件时,我可以看到我正在编写的“标题”中的值(取自第一行)不对应到其他行。

from collections import defaultdict as dd
d = dd(lambda: dd(float))

input =  open("input.txt")
output = open("output.txt","w")
while 1:
    line = input.readline()
    if not line:
        break
    line = line.strip('\n').strip('\r')
    splitLine = line.split(',')
    if (len(splitLine) <3):
        break
    d[splitLine[0]][splitLine[1]] = splitLine[2]

output.write("\t")
for k,v in d.items()[0][1].items():
    output.write(str(k)+"\t")
output.write("\n")
for k,v in d.items():
    output.write(k+"\t")
    for k2,v2 in v.items():
        output.write(str(v2)+"\t")
    output.write("\n")

6 个答案:

答案 0 :(得分:1)

当你拥有的只是一把锤子。 。 。 。

从概念上讲,您尝试做的很简单,但由于数据的大小,计算难度很大。我倾向于使用R来获得它的分析和图形容量,而不是它的数据争论技能。当我需要移动大量数据时,我通常只是将所有内容都放在数据库中。

最近我在SQLite和R上取得了相当大的成功。最好的部分是你可以实际使用R来读取数据,这样可以轻松导入大型SPSS文件或其他SQLite数据源。真的可以处理,但R可以。

http://cran.r-project.org/web/packages/RSQLite/index.html

这是我推荐的工作流程。

  1. 将您的数据导入R.(完成)
  2. 库(RSQLite)
  3. 将数据框移至SQLite。
  4. 在A列和B列上创建索引。
  5. 创建一个构建表格的视图。
  6. 从R查询您的视图并将返回强制转换为表格。

答案 1 :(得分:1)

在R中,我可以这样做:

N <- 1000000
x <- sample(1:400,N,TRUE)
y <- sample(1:400,N,TRUE)
z <- sample(1:400,N,TRUE)

w <- table(x,y,z)

内存峰值低于800MB。

那么你有什么限制?


EDIT。这段R代码的和平:

N <- 1000000
mydata <- data.frame(
    A=sample(runif(400),N,TRUE),
    B=sample(runif(400),N,TRUE),
    C=runif(N)
)

require(reshape)
results <- cast(mydata, A~B, value="C")
write.table(as.matrix(results),na="",sep="\t",file="results.txt")

用少于300MB的RAM创建你想要的东西。

在我的数据上它会发出警告,因为有非独特的A-B组合,但对你来说应该没问题。

答案 2 :(得分:1)

全新的故事值得一个全新的答案。

不需要defaultdict,甚至不需要defaultdict,因为不小心使用它会像死星的拖拉机光束一样吸入记忆。

此代码未经测试,甚至可能无法编译;我可能在某处交换了行和列;以后修复/解释......必须赶紧......

d = {}
col_label_set = set()
row_label_set = set()
input =  open("input.txt")
output = open("output.txt","w")
for line in input:
    line = line.strip()
    splat = line.split(',')
    if len(splat) != 3:
        break # error message???
    k1, k2, v = splat
    try:
        subdict = d[k1]
    except KeyError:
        subdict = {}
        d[k1] = subdict
    subdict[k2] = v
    row_label_set.add(k1)
    col_label_set.add(k2)
col_labels = sorted(col_label_set)
row_labels = sorted(row_label_set
output.write("\t")
for v in col_labels::
    output.write(v + "\t")
output.write("\n")
for r in row_labels:
    output.write(r + "\t")
    for c in col_labels:
        output.write(d[r].get(c, "") + "\t")
    output.write("\n")

更新这是一个修复和重构的版本,测试范围如下:

class SparseTable(object):

    def __init__(self, iterable):
        d = {}
        col_label_set = set()
        for row_label, col_label, value in iterable:
            try:
                subdict = d[row_label]
            except KeyError:
                subdict = {}
                d[row_label] = subdict
            subdict[col_label] = value
            col_label_set.add(col_label)
        self.d = d
        self.col_label_set = col_label_set

    def tabulate(self, row_writer, corner_label=u"", missing=u""):
        d = self.d
        col_labels = sorted(self.col_label_set)
        row_labels = sorted(d.iterkeys())
        orow = [corner_label] + col_labels
        row_writer(orow)
        for row_label in row_labels:
            orow = [row_label]
            subdict = d[row_label]
            for col_label in col_labels:
                orow.append(subdict.get(col_label, missing))
            row_writer(orow)

if __name__ == "__main__":

    import sys

    test_data = u"""
    3277,4733,54.1
    3278,4741,51.0
    3278,4750,28.4
    3278,4768,36.0
    3278,4776,50.1
    3278,4784,51.4
    3279,4792,82.6
    3279,4806,78.2
    3279,4814,36.4
    """.splitlines(True)

    def my_writer(row):
        sys.stdout.write(u"\t".join(row))
        sys.stdout.write(u"\n")

    def my_reader(iterable):
        for line in iterable:
            line = line.strip()
            if not line: continue
            splat = line.split(u",")
            if len(splat) != 3:
                raise ValueError(u"expected 3 fields, found %d" % len(splat))
            yield splat

    table = SparseTable(my_reader(test_data))
    table.tabulate(my_writer, u"A/B", u"....")

这是输出:

A/B     4733    4741    4750    4768    4776    4784    4792    4806    4814
3277    54.1    ....    ....    ....    ....    ....    ....    ....    ....
3278    ....    51.0    28.4    36.0    50.1    51.4    ....    ....    ....
3279    ....    ....    ....    ....    ....    ....    82.6    78.2    36.4

答案 3 :(得分:0)

如果你可以在R中使用table(x,y,z),那么如何尝试处理这些庞大数据集的R内存包呢?使用程序包bigmemory中的read.big.matrix函数读取数据集和程序包bigtabulate中的bigtable函数以创建表。

请参阅vignettes

答案 4 :(得分:0)

您想要的输出示例对我来说看起来不像是一个三向列联表。这将是从(key1,key2,key3)到出现次数的映射。您的示例看起来像是从(key1,key2)到某个数字的映射。 你没有说当(key1,key2)重复时该做什么:平均,总计,还有什么?

假设您想要使用嵌套的defaultdict来实现总体,这是一种节省内存的方法:

>>> from collections import defaultdict as dd
>>> d = dd(lambda: dd(float))
>>> d[3277][4733] += 54.1
>>> d
defaultdict(<function <lambda> at 0x00D61DF0>, {3277: defaultdict(<type 'float'>, {4733: 54.1})})
>>> d[3278][4741] += 51.0
>>> d
defaultdict(<function <lambda> at 0x00D61DF0>, {3277: defaultdict(<type 'float'>, {4733: 54.1}), 3278: defaultdict(<type 'float'>, {4741: 51.0})})
>>>

使用单个defaultdict和复合键的另一种方法:

>>> d2 = dd(float)
>>> d2[3277,4733] += 54.1
>>> d2
defaultdict(<type 'float'>, {(3277, 4733): 54.1})
>>> d2[3278,4741] += 51.0
>>> d2
defaultdict(<type 'float'>, {(3277, 4733): 54.1, (3278, 4741): 51.0})
>>>

如果您在将这些数据组合在一起后说出要对这些数据做什么,这可能会有所帮助......

如果你想要(例如)一个平均值,你有两个选择:(1)两个数据结构,一个用于总计,一个用于计数,然后执行“average = total - count”(2)对数据进行排序前两列,用户itertools.groupby一起收集重复项,进行计算,并将结果添加到“平均”数据结构中。哪种方法使用较少的内存很难说; Python是Python,你可以很快地尝试。

答案 5 :(得分:0)

dict的一个小子类可以为您提供一个使用该表的舒适对象。 在桌面PC上500.000项应该不是问题 - 如果你碰巧有500.000.000项,类似的类可以从键映射到文件本身的位置(实现起来会更酷:-))< / p>

import csv
class ContingencyTable(dict):
    def __init__(self):
        self.a_keys=set()
        self.b_keys=set()
        dict.__init__(self)
    def __setitem__(self, key,value):
        self.a_keys.add(key[0])
        self.b_keys.add(key[1])
        dict.__setitem__(self, key, value)
    def feed(self, file):
        reader = csv.reader(file)
        reader.next()
        for a, b, c in reader:
            self[int(a),int(b)] = float(c)

table = ContingencyTable()
table.feed(open("yourfile.csv"))