2016-01-26 2 views
1

Я использую pandas в Python 3.4 для определения совпадений между двумя кадрами данных. Матчи основаны на строгом равенстве, за исключением последнего столбца, где близкие совпадения (+/- 5) являются точными.Pandas Merge vs. Boolean Indexing

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

Сначала я пошел с конкретным решением булевых индексирования, но это заняло некоторое время, чтобы перехватить все данные, поэтому я опробовал функцию слияния pandas. Однако моя реализация слияния еще медленнее в моих тестовых данных. Он работает от 2 до 4 раз медленнее, чем булевское индексирование.

Вот тестовый запуск:

import pandas as pd 
import random 
import time 

def make_lsts(lst, num, num_choices): 
    choices = list(range(0,num_choices)) 
    [lst.append(random.choice(choices)) for i in range(0,num)] 
    return lst 

def old_way(test, data): 
    t1 = time.time() 
    tmp = data[(data.col_1 == test.col_1[0]) & 
       (data.col_2 == test.col_2[0]) & 
       (data.col_3 == test.col_3[0]) & 
       (data.col_4 == test.col_4[0]) & 
       (data.col_5 == test.col_5[0]) & 
       (data.col_6 == test.col_6[0]) & 
       (data.col_7 == test.col_7[0]) & 
       (data.col_8 >= (test.col_8[0]-5)) & 
       (data.col_8 <= (test.col_8[0]+5))] 
    t2 = time.time() 
    print('old time:', t2-t1) 

def new_way(test, data): 
    t1 = time.time() 
    tmp = pd.merge(test, data, how='inner', sort=False, copy=False, 
        on=['col_1', 'col_2', 'col_3', 'col_4', 'col_5', 'col_6', 'col_7']) 
    tmp = tmp[(tmp.col_8_y >= (test.col_8[0] - 5)) & (tmp.col_8_y <= (test.col_8[0] + 5))] 
    t2 = time.time() 
    print('new time:', t2-t1) 

if __name__ == '__main__': 
    t1 = time.time() 
    data = pd.DataFrame({'col_1':make_lsts([], 4000000, 7), 
         'col_2':make_lsts([], 4000000, 3), 
         'col_3':make_lsts([], 4000000, 3), 
         'col_4':make_lsts([], 4000000, 5), 
         'col_5':make_lsts([], 4000000, 4), 
         'col_6':make_lsts([], 4000000, 4), 
         'col_7':make_lsts([], 4000000, 2), 
         'col_8':make_lsts([], 4000000, 20)}) 

    test = pd.DataFrame({'col_1':[1], 'col_2':[1], 'col_3':[1], 'col_4':[4], 'col_5':[0], 'col_6':[1], 'col_7':[0], 'col_8':[12]}) 
    t2 = time.time() 
    old_way(test, data) 
    new_way(test, data) 
    print('time building data:', t2-t1) 

На моем последнем счете я вижу следующее:

# old time: 0.2209608554840088 
# new time: 0.9070699214935303 
# time building data: 75.05818915367126 

Обратите внимание, что даже новый метод с функцией объединения использует логическое индексирование на последнем столбец, посвященный диапазону значений, но я думал, что слияние может сделать тяжелый подъем в проблеме. Это явно не так, поскольку слияние по первым столбцам занимает почти все время, используемые в новом методе.

Возможно ли оптимизировать мою реализацию функции слияния? (Исходя из таблицы R и data.table, я потратил 30 минут на неудачный поиск способа установить ключ в кадре данных pandas.) Является ли это просто проблемой, которая слияния не подходит для обработки? Почему логическое индексирование выполняется быстрее, чем слияние в этом примере?

Я не совсем понимаю память этих подходов, поэтому понимание проницательности.

ответ

1

Хотя вы можете объединиться в любом наборе столбцов, производительность слияния будет лучше всего при объединении индексов.

Если заменить

tmp = pd.merge(test, data, how='inner', sort=False, copy=False, 
       on=['col_1', 'col_2', 'col_3', 'col_4', 'col_5', 'col_6', 'col_7']) 

с

cols = ['col_%i' % (i+1) for i in xrange(7)] 
test.set_index(cols, inplace=True) 
data.set_index(cols, inplace=True) 
tmp = pd.merge(test, data, how='inner', left_index=True, right_index=True) 
test.reset_index(inplace=True) 
data.reset_index(inplace=True) 

ли это работать быстрее? Я не тестировал его, но я думаю, что это должно помочь ...

Индексируя столбцы, которые вы хотите объединить, DataFrame организует данные под капотом таким образом, что он знает, где найти значения быстрее, чем если бы данные были просто в обычных столбцах.

+0

Спасибо за подсказку об индексировании @SPKoder. Это имеет для меня большой смысл, но производительность действительно уменьшилась. Выполнение этого кода заняло около 7 секунд (вместо <1 секунды выполнения). Я думал, что это может быть связано с индексированием и переиндексацией через 4 000 000 строк, поэтому я приурочен только к слиянию, и эта операция была около 7 секунд. Любая идея, почему время слияния увеличилось бы? – 3novak

+0

Хм. Это удивительно. Изменяет ли настройка copy = False? Я сомневаюсь в этом, но, возможно, стоит попробовать ... Кроме того, в этом случае правильный результат DataFrame? Какие-либо дополнительные строки создаются? – SPKoder

+0

Нет, использование 'copy = False' и' sort = False' не влияет на время. И, да, кадр данных правильный. Я выполнил инструкции [здесь] (http://pandas.pydata.org/pandas-docs/stable/categorical.html), чтобы лучше индексировать столбцы, и это помогло вернуть его под второй, но все равно медленнее, чем логическое индексирование. – 3novak

Смежные вопросы