Мне нужно аккуратное предложение структуры данных для хранения очень большого набора данных (для обучения Наивного Байеса в Python) - PullRequest
1 голос
/ 17 сентября 2011

Я собираюсь внедрить наивный байесовский классификатор с 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)
  • слово и его частота на каждое электронное письмо
  • Мне также нужно создать совокупность всех слов и их частоты с информацией метки

Есть предложения по такой структуре данных?

Ответы [ 4 ]

0 голосов
/ 17 сентября 2011

Я бы сгенерировал класс

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,  ... } 

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

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

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

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

0 голосов
/ 17 сентября 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}
0 голосов
/ 17 сентября 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
0 голосов
/ 17 сентября 2011

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

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

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

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

...