0

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

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“table(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")
4

6 回答 6

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
于 2010-10-29T11:26:29.153 回答
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 中查询您的视图并将返回值强制转换为表。
于 2010-10-28T16:21:13.520 回答
1

在 RI 中可以这样做:

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。

那么你有什么限制呢?


编辑。这个和平的 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 创建您想要的内容。

根据我的数据,它给出了警告,因为存在非唯一的 AB 组合,但对你来说应该没问题。

于 2010-10-28T16:24:47.263 回答
0

对我来说,您期望的输出示例看起来不像 3 路列联表。那将是从 (key1, key2, key3) 到出现次数的映射。您的示例看起来像是从 (key1, key2) 到某个数字的映射。您没有说 (key1, key2) 重复时该怎么做:平均,总计,其他?

假设您想要一个总数,这是 Python 中的一种节省内存的方法,使用嵌套defaultdict的 s:

>>> 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)两个数据结构,一个用于总计,一个用于计数,然后执行“平均 = 总计 - 计数”(2)对前 2 列的数据进行排序,用户 itertools.groupby 将您的重复项收集在一起,进行计算,并将结果添加到您的“平均”数据结构中。很难说这些方法中哪一种会使用更少的内存。Python 是 Python,你可以很快地尝试两者。

于 2010-10-28T20:09:07.360 回答
0

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

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"))
于 2010-10-28T20:16:17.067 回答
0

如果您可以table(x,y,z)在 R 中使用,那么尝试一下处理如此庞大数据集的 R 内存不足包怎么样?使用read.big.matrixbigmemory中的函数读取数据集,使用包bigtabulatebigtable中的函数创建表。

小插曲

于 2010-10-28T16:23:45.307 回答