Скрипт для преобразования огромной таблицы из трех столбцов в таблицу - PullRequest
0 голосов
/ 28 октября 2010

У меня есть набор данных (файлы 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 строк, около 400 различных факторов в A и B. (Ограничения OOo, MSO и R не позволяют этого достичь)

Я уверен, что для создания такой таблицы можно использовать скрипт Pythonи A, и B являются числами (но могут рассматриваться как строки).

Кто-нибудь имел дело с этим? (псевдокод или код на C или Java также приветствуется ... но я предпочитаю python, поскольку он быстреереализовать:)

Редактировать: Почти есть, благодаря Джону Мачину. Следующий скрипт 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 ]

1 голос
/ 29 октября 2010

Вся новая история заслуживает совершенно нового ответа.

Не нужно 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
1 голос
/ 28 октября 2010

В 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)

И пик памяти ниже, чем 800 МБ.

Итак, какие у вас ограничения?


РЕДАКТИРОВАТЬ.Этот код 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")

создайте то, что вам нужно, с объемом оперативной памяти менее 300 МБ.

По моим данным это дает предупреждение, поскольку существуют неуникальные комбинации AB, но для васдолжно быть в порядке.

1 голос
/ 28 октября 2010

Когда у тебя есть только молоток. , , , .

Концептуально, то, что вы пытаетесь сделать, просто, но из-за размера ваших данных, это сложно в вычислительном отношении. Я склонен использовать R для аналитических и графических возможностей, а не для обработки данных. Когда мне нужно перемещать кучу данных, я обычно просто помещаю все в базу данных.

В последнее время я довольно успешно работал с SQLite и R. Самое приятное, что вы можете использовать R для чтения ваших данных, что упрощает импорт больших файлов SPSS или других источников данных, которые SQLite может на самом деле не справиться, но R не может.

http://cran.r -project.org / веб / пакеты / RSQLite / index.html

Вот мой рекомендуемый рабочий процесс.

  1. Импорт данных в R. (Готово)
  2. Библиотека (RSQLite)
  3. Переместите фрейм данных в SQLite.
  4. Создание индексов для столбцов A и B.
  5. Создайте представление для построения вашей таблицы.
  6. Запрос вашего представления из R и приведение возвращений в таблицу.
0 голосов
/ 29 октября 2010

Небольшой подкласс dict может предоставить вам удобный объект для работы со столом.500 000 элементов не должны быть проблемой на настольном ПК - если у вас есть 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"))
0 голосов
/ 29 октября 2010

Ваш пример желаемого результата не выглядит для меня как трехсторонняя таблица непредвиденных обстоятельств. Это было бы сопоставление (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, вы можете попробовать оба варианта довольно быстро.

0 голосов
/ 28 октября 2010

Если бы вы могли использовать table(x,y,z) в R, то как насчет того, чтобы попробовать R из пакетов памяти, которые обрабатывают такие огромные наборы данных?Используйте функцию read.big.matrix в пакете bigmemory для чтения в наборе данных и функцию bigtable в пакете bigtabulate для создания таблицы.

См. виньетка .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...