Оптимизация комбинации itertools с сгруппированным DataFrame и пост-фильтром - PullRequest
3 голосов
/ 04 марта 2020

У меня есть DataFrame как

Locality     money
1            3
1            4
1            10
1            12
1            15
2            16
2            18

Мне нужно сделать комбинацию с заменой денежного столбца с групповым представлением на местности и фильтр на разницу денег. Цель должна быть такой, как

Locality     money1     money2
1            3          3 
1            3          4
1            4          4
1            10         10
1            10         12
1            10         15
1            12         12
1            12         15
1            15         15
2            16         16
2            16         18
2            18         18

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

Мой текущий код

from itertools import combinations_with_replacement
import numpy as np
import panda as pd

def generate_graph(input_series, out_cols):
    return pd.DataFrame(list(combinations_with_replacement(input_series, r=2)), columns=out_cols)

df = (
    df.groupby(['Locality'])['money'].apply(
        lambda x: generate_graph(x, out_cols=['money1', 'money2'])
    ).reset_index().drop(columns=['level_1'], errors='ignore')
)

# Ensure the Distance between money is within the permissible limit
df = df.loc[(
    df['money2'] - df['money1'] < 6
)]

Проблема в том, что у меня есть DataFrame с 100000 строками, что занимает почти 33 секунд для обработки моего кода. Мне нужно оптимизировать время, затрачиваемое моим кодом, вероятно, используя numpy. Я ищу для оптимизации группового и пост-фильтра, который занимает дополнительное пространство и время. Для демонстрационных данных вы можете использовать этот код для генерации DataFrame.

# Generate dummy data
t1 = list(range(0, 100000))
b = np.random.randint(100, 10000, 100000)
a = (b/100).astype(int)

df = pd.DataFrame({'Locality': a, 'money': t1})
df = df.sort_values(by=['Locality', 'money'])

1 Ответ

2 голосов
/ 04 марта 2020

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

Вместо постфильтрация - применить расширенную функцию (скажем, combine_values), которая генерирует кадр данных для выражение генератора , получающее уже отфильтрованные (по условию) комбинации.

(* factor ниже - это аргумент по умолчанию, который указывает на упомянутый допустимый предел )

In [48]: def combine_values(values, out_cols, factor=6): 
    ...:     return pd.DataFrame(((m1, m2) for m1, m2 in combinations_with_replacement(values, r=2)  
    ...:                         if m2 - m1 < factor), columns=out_cols) 
    ...:                                                                                                                                         

In [49]: df_result = ( 
    ...:     df.groupby(['Locality'])['money'].apply( 
    ...:         lambda x: combine_values(x, out_cols=['money1', 'money2']) 
    ...:     ).reset_index().drop(columns=['level_1'], errors='ignore') 
    ...: ) 

Время исполнения Производительность:

In [50]: %time df.groupby(['Locality'])['money'].apply(lambda x: combine_values(x, out_cols=['money1', 'money2'])).reset_index().drop(columns=['l
    ...: evel_1'], errors='ignore')                                                                                                              
CPU times: user 2.42 s, sys: 1.64 ms, total: 2.42 s
Wall time: 2.42 s
Out[50]: 
        Locality  money1  money2
0              1      34      34
1              1     106     106
2              1     123     123
3              1     483     483
4              1     822     822
...          ...     ...     ...
105143        99   99732   99732
105144        99   99872   99872
105145        99   99889   99889
105146        99   99913   99913
105147        99   99981   99981

[105148 rows x 3 columns]
...