Pytables дублирует 2,5 гига строк - PullRequest
0 голосов
/ 21 марта 2020

В настоящее время у меня есть файл .h5 с таблицей в нем, состоящей из трех столбцов: текстовые столбцы из 64 символов, столбец UInt32, относящийся к источнику текста, и столбец UInt32, который является xxha sh из текст. Таблица состоит из ~ 2.5e9 строк

Я пытаюсь найти и сосчитать дубликаты каждой текстовой записи в таблице - по сути, объединить их в одну запись при подсчете экземпляров. Я попытался сделать это, проиндексировав столбец ha sh, а затем перебрав table.itersorted(hash), отслеживая значение ha sh и проверив наличие коллизий - очень похоже на поиск дубликата в pytable hdf5 с 500e6 строк . Я не изменял таблицу, пока просматривал ее, а записал объединенные записи в новую таблицу - я помещаю код внизу.

По сути, у меня проблема в том, что весь процесс занимает значительно слишком долго - мне потребовалось около 20 часов, чтобы добраться до итерации № 5 4e5. Однако я работаю над жестким диском, поэтому вполне возможно, что узкое место есть. Видите ли вы, как я могу улучшить свой код, или вы можете предложить другой подход? Заранее благодарю за любую помощь.

PS Я обещаю, что не делаю ничего противозаконного, это просто крупномасштабный анализ утечек паролей для моей дипломной работы бакалавра.

ref = 3 #manually checked first occuring hash, to simplify the below code
gen_cnt = 0
locs = {}


print("STARTING")
for row in table.itersorted('xhashx'):
    gen_cnt += 1 #so as not to flush after every iteration
    ps = row['password'].decode(encoding = 'utf-8', errors = 'ignore')

    if row['xhashx'] == ref:
        if ps in locs:
            locs[ps][0] += 1
            locs[ps][1] |= row['src']

        else:
            locs[ps] = [1, row['src']]


    else:
        for p in locs:
            fill_password(new_password, locs[ps]) #simply fills in the columns, with some fairly cheap statistics procedures
            new_password.append()   

        if (gen_cnt > 100):
            gen_cnt = 0
            new_table.flush()  

        ref = row['xhashx']```


1 Ответ

0 голосов
/ 22 марта 2020

Ваш набор данных в 10 раз больше, чем указанное решение (строки 2.5e9 против 500e6). Вы провели какое-либо тестирование, чтобы определить, на что тратится время? Метод table.itersorted() может быть не линейным и может потребовать значительных ресурсов. (У меня нет опыта работы с itersorted.)

Вот процесс, который может быть быстрее:

  1. Извлечь массив NumPy из поля ha sh ( column xhashx)
  2. Найдите уникальные значения ha sh
  3. L oop через уникальные значения ha sh и извлеките массив NumPy строк, соответствующих каждому значению
  4. Проведите тесты уникальности для строк в этом извлеченном массиве
  5. Запишите уникальные строки в ваш новый файл

Код этого процесса приведен ниже:
Примечание: это не было проверено, поэтому может иметь небольшой синтаксис или лог c пробелы

# Step 1: Get a Numpy array of the 'xhashx' field/colmu only:
hash_arr = table.read(field='xhashx')
# Step 2: Get new array with unique values only:
hash_arr_u = np.unique(hash_arr)

# Alternately, combine first 2 steps in a single step
hash_arr_u = np.unique(table.read(field='xhashx'))

# Step 3a: Loop on rows unique hash values
for hash_test in hash_arr_u :

# Step 3b: Get an array with all rows that match this unique hash value
     match_row_arr = table.read_where('xhashx==hash_test')

# Step 4: Check for rows with unique values
# Check the hash row count. 
# If there is only 1 row, uniqueness tested not required  
     if match_row_arr.shape[0] == 1 :
     # only one row, so write it to new.table

     else :
     # check for unique rows
     # then write unique rows to new.table

##################################################
# np.unique has an option to save the hash counts
# these can be used as a test in the loop
(hash_arr_u, hash_cnts) = np.unique(table.read(field='xhashx'), return_counts=True)

# Loop on rows in the array of unique hash values
for cnt in range(hash_arr_u.shape[0]) :

# Get an array with all rows that match this unique hash value
     match_row_arr = table.read_where('xhashx==hash_arr_u(cnt)')

# Check the hash row count. 
# If there is only 1 row, uniqueness tested not required  
     if hash_cnts[cnt] == 1 :
     # only one row, so write it to new.table

     else :
     # check for unique rows
     # then write unique rows to new.table
...