Мне нужно предложение по аккуратной структуре данных для хранения очень большого набора данных (для обучения Naive Bayes в Python)

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

1 9:3 94:1 109:1 163:1 405:1 406:1 415:2 416:1 435:3 436:3 437:4 ...

Где 1 — метка (спам, не спам), и каждая пара соответствует слову и его частоте. Например. 9:3 соответствует слову 9 и встречается в этом образце письма 3 раза.

Мне нужно прочитать этот набор данных и сохранить его в структуре. Поскольку это очень большой и разреженный набор данных, я ищу аккуратную структуру данных для хранения следующих переменных:

  • индекс каждого письма
  • метка этого (1 или -1)
  • слово и его частота на каждое электронное письмо
  • Мне также нужно создать корпус всех слов и их частоты с информацией о метках

Любые предложения для такой структуры данных?


person Lucas    schedule 17.09.2011    source источник
comment
Если вы говорите хранить его в структуре, вы имеете в виду структуру, которая находится в памяти? Или вы хотите сохранить его на диске?   -  person rocksportrocker    schedule 17.09.2011
comment
Кажется, вы упустили ключевой элемент: насколько велик ваш набор данных? Сколько записей и сколько слов?   -  person Michael J. Barber    schedule 17.09.2011


Ответы (4)


я бы создал класс

class Document(object):

    def __init__(self, index, label, bowdict):
        self.index = index
        self.label = label
        self.bowdict = bowdict

Вы сохраняете свой разреженный вектор в bowdict, например

{ 9:3, 94:1, 109:1,  ... } 

и храните все свои данные в списке Documents

Чтобы получить сводку обо всех документах с заданным ярлыком:

from collections import defaultdict

def aggregate(docs, label):
    bow = defaultdict(int)
    for doc in docs:
        if doc.label == label:
           for (word, counter) in doc.bowdict.items():
                bow[word] += counter  
    return bow    

Вы можете сохранить все свои данные с помощью модуля cPickle.

Другой подход заключается в использовании http://docs.scipy.org/doc/scipy/reference/sparse.html. Вы можете представить вектор лука как разреженную матрицу с одной строкой. Если вы хотите объединить луки, вам просто нужно их сложить. Это может быть намного быстрее, чем простое решение выше.

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

person rocksportrocker    schedule 17.09.2011

Если вы предполагаете, что вас не заботит многократное вхождение каждого слова в электронное письмо, то все, что вам действительно нужно знать, это (то есть ваши функции являются логическими):

Каково количество положительных и отрицательных ассоциаций для каждой функции?

Вы можете сделать это онлайн очень легко за один проход, отслеживая только эти два числа для каждой функции.

Небулевы функции означают, что вам придется каким-то образом дискретизировать функции, но на самом деле вы не спрашиваете, как это сделать.

person Rob Neuhaus    schedule 17.09.2011

https://github.com/Yelp/sqlite3dbm
http://packages.python.org/sqlite3dbm/

Это похоже на словарь Python, за исключением того, что он хранит все, что вы ему даете, на диске и поэтому является постоянным! Это не раздует тонны памяти, потому что это записывает данные на диск. Вы можете настроить эти файлы в одной программе, а использовать их для классификации в другой, не беспокоясь о проблемах с сериализацией.

Вы можете четко смоделировать первую проблему как

doc_to_info[doc_id] = {'label': 'label_0', 'word_freqs': {'this': 3, 'is': 4, ...}}

Вы можете смоделировать вторую проблему как

word_to_freq[word] = {'label_0': 42, 'label_1': 314}
person Aditya Mukherji    schedule 17.09.2011

Я бы начал с какой-нибудь реляционной базы данных (SQLite легко настроить) и использовал бы следующую структуру таблиц:

Word
-----
Number    INT   -- The word number in your data
Word      TEXT  -- The word itself


Entry
-----
ID        INT  -- Some number to make it unique
Spam      INT  -- -1 or 1 as you described


Entry_Word
----------
EntryID   INT  -- The entry this row corresponds to
WordNo    INT  -- The number of the word
Frequency INT  -- The number of occurences of the word

Чтобы получить записи, вы можете использовать

SELECT ID, Spam
FROM Entry

Чтобы получить частоты слов для некоторой записи, вы можете использовать:

SELECT WordNo, Frequency
FROM Entry_Word
WHERE EntryID = ?

Чтобы получить корпус частоты слов, вы можете использовать:

SELECT
    WordNo,
    SUM(MIN(0,Spam*Frequency)) AS NotSpamFrequency,
    SUM(MAX(0,Spam*Frequency)) AS SpamFrequency
FROM Entry
INNER JOIN Entry_Word ON EntryID = ID
GROUP BY WordNo

Вы также можете включить само слово, если хотите:

SELECT
    Word,
    WordNo,
    SUM(MIN(0,Spam*Frequency)) AS NotSpamFrequency,
    SUM(MAX(0,Spam*Frequency)) AS SpamFrequency
FROM Entry
INNER JOIN Entry_Word ON EntryID = ID
LEFT JOIN Word ON Number = WordNo
GROUP BY Word, WordNo
person Markus Jarderot    schedule 17.09.2011