Как перечислить все пары чисел, попадающие в группу диапазонов? - PullRequest
0 голосов
/ 09 июня 2018

Предположим, у меня есть фрейм данных df1, который включает в себя два столбца - A & B. Значение A представляет нижний диапазон, а значение B представляет верхний диапазон.

  A     B
10.5  20.5
30.5  40.5
50.5  60.5

У меня есть еще один фрейм данных, который включает двастолбцы - C & D, содержащие другой диапазон чисел.

  C     D
12.34  15.90
13.68  19.13
33.5   35.60
35.12  38.76
50.6   59.1

Теперь я хочу перечислить все пары из df2, которые попадают под группы (между нижним и верхним диапазоном) в df1.

Конечный результат должен быть таким -

     Key                Values
(10.5, 20.5)  [(12.34, 15.90), (13.68, 19.13)]
(30.5, 40.5)  [(33.5, 35.60), (35.12, 38.76)]
(50.5, 60.5)  [(50.6, 59.1)]

Решение должно быть эффективным, так как у меня 5000 групп диапазона и 85000 чисел из другого диапазона.

Ответы [ 4 ]

0 голосов
/ 09 июня 2018

Одним из решений является использование apply, например:

# first create your output DF with the keys from your df with A and B
df = pd.DataFrame({'Key':[(a,b) for a,b in df1.itertuples(index=False)]})
# define a function to find the range in df2 within the range from the Keys column
def find_range( key, df_2):
    mask = (key[0] <= df_2['C']) & (key[1] >= df_2['D'])
    return [(c,d) for c,d in df_2[mask].itertuples(index=False)]
#now create the column Values with apply
df['Values'] = df['Key'].apply(find_range, args=(df2,))
# output
            Key                           Values
0  (10.5, 20.5)  [(12.34, 15.9), (13.68, 19.13)]
1  (30.5, 40.5)                   [(33.5, 35.6)]

Примечание: я предполагаю, что в ваших данных столбец C всегда ниже, чем D, если нет, вам нужно изменить маску вфункция для проверки того, находятся ли C и D в пределах клавиши [0] и клавиши [1].Кроме того, у меня не было всего вашего ввода, поэтому значения для строки № 1 отличаются от того, что вы показываете, но только разница ввода.

0 голосов
/ 09 июня 2018

Это не слишком быстро (~ 30 секунд) на моем компьютере), но его можно легко ускорить с помощью пакета multiprocessing, если у вас несколько ядер.

Генерация данных:

def get_fake(n):
    df = pd.DataFrame(np.random.rand(n * 2).reshape(-1, 2))
    df.loc[:, 1] += 1
    return df

df1 = get_fake(200)
df2 = get_fake(90000)

Затем для обрабатывающей части:

from collections import defaultdict
result = defaultdict(list)
for index, start, stop in df1.itertuples():
    subdf = df2[(start < df2.iloc[:, 0]) & (df2.iloc[:, 1] < stop)]
    result[(start, stop)] += subdf.values.tolist()

Результат является диктом, но при необходимости может быть легко преобразован в серию.

0 голосов
/ 09 июня 2018

Это будет легко, если вы используете interval index, т. Е.

idx = pd.IntervalIndex.from_arrays(df['A'],df['B'])
keys = df.values.tolist()
values = df2.groupby(df.loc[idx.get_indexer(df2['C'])].index).apply(lambda x : x.values)

new_df = pd.DataFrame({'key': keys , 'value': values})

          key                            value
0  [10.5, 20.5]  [[12.34, 15.9], [13.68, 19.13]]
1  [30.5, 40.5]   [[33.5, 35.6], [35.12, 38.76]]
2  [50.5, 60.5]                   [[50.6, 59.1]]

Доступ к данным на основе индекса интервала даст вам ключи, так что вы сможете группировать и агрегировать, т.е.

df.loc[idx.get_indexer(df2['C'])]
     A     B
0  10.5  20.5
0  10.5  20.5
1  30.5  40.5
1  30.5  40.5
2  50.5  60.5
0 голосов
/ 09 июня 2018

Попробуйте следующий код:

df = pd.DataFrame()
df['Key'] = [(row['A'],row['B']) for idx,row in df1.iterrows()]
values_col_test = [(c1,c2) for c1,c2 in df2.itertuples(index=False)]
values_col = []

for i in range(0,len(values_col_test),2):
     try:
          values_col.append(list(values_col_test[i:i+2]))
     except:
          values_col.append(list(values_col_test[i]))

df['Value'] = values_col
print(df)

Вывод:

            Key                           Value
0  (10.5, 20.5)   [(2.34, 11.9), (3.68, 19.13)]
1  (30.5, 40.5)  [(33.5, 35.6), (35.12, 38.76)]
2  (50.5, 60.5)                  [(50.6, 59.1)]
...