В Pandas объедините две dataframs со сложным мультииндексированием

77
8

Я хотел бы объединить два кадра данных в столбцах Name и Depth. Глубина в левом df имеет один столбец глубины ("глубина"). Однако правый df имеет два столбца глубины ('top_depth' и 'bottom_depth').

Я хотел бы взять каждую запись из левого df и, если доступно, назначить запись из права df, если "глубина" находится между "top_depth" и "bottom_depth".

Я собрал несколько простых фреймов:

df1 = pd.DataFrame(np.array([
['b1', 4, 9],
['b1', 5, 61],
['b1', 15, 95],
['b1', 24, 9],
['b2', 4, 5],
['b2', 6, 6],
['b2', 44, 0]]),
columns=['name', 'depth', 'attr1'])
df2 = pd.DataFrame(np.array([
['b1', 1, 6, 66],
['b1', 14, 16, 99],
['b1', 51, 55, 9],
['b3', 0, 5, 32]]),
columns=['name', 'top_depth', 'bottom_depth', 'attr2'])

Затем слейте, чтобы получить это:

>>> df3
name depth top_depth bottom_depth attr1 attr2
0 b1 4.0 1.0 6.0 9.0 66.0
1 b1 5.0 1.0 6.0 61.0 66.0
2 b1 15.0 14.0 16.0 95.0 99.0
3 b1 24 NaN NaN 9 NaN
4 b2 4 NaN NaN 5 NaN
5 b2 6 NaN NaN 6 NaN
6 b2 44 NaN NaN 0 NaN

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

спросил(а) 2021-01-25T20:09:02+03:00 4 месяца, 3 недели назад
1
Решение
99

Вы можете присоединиться (по индексу):

In [11]: df1.join(df2, how='outer', rsuffix='_')
Out[11]:
name depth attr1 name_ top_depth bottom_depth attr2
0 b1 4 9 b1 1 6 66
1 b1 5 61 b1 14 16 99
2 b1 15 95 b1 55 51 9
3 b1 24 9 b3 0 5 32
4 b2 4 5 NaN NaN NaN NaN
5 b2 6 6 NaN NaN NaN NaN
6 b2 44 0 NaN NaN NaN NaN

Примечание: rsuffix требуется, поскольку столбцы имен не совпадают... неясно, что вы хотели бы сделать с этим случаем.

Примечание: np.array заставляет массивы совместно использовать тип (начальный?), В этом случае это означает, что все числа являются строками. Вы можете передать простой список python в DataFrame!

Здесь немного неэффективный метод, сначала есть функция, которая ищет имя и проверяет, что глубина находится сверху и снизу:

def get_depth_group(name, depth):
arr = (df2.name == name) & (df2.bottom_depth > depth) & (depth > df2.top_depth)
return df2.iloc[arr.argmax()] if any(arr) else np.nan

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

In [21]: df1[['depth', 'attr1']].join(df1.apply(lambda x: get_depth_group(x['name'], x['depth']), axis=1))
Out[21]:
depth attr1 name top_depth bottom_depth attr2
0 4 9 b1 1 6 66
1 5 61 b1 1 6 66
2 15 95 b1 14 16 99
3 24 9 NaN NaN NaN NaN
4 4 5 NaN NaN NaN NaN
5 6 6 NaN NaN NaN NaN
6 44 0 NaN NaN NaN NaN

ответил(а) 2021-01-25T20:09:02+03:00 4 месяца, 3 недели назад
45

Частичное:

import pandas as pd
import numpy as np

df1 = pd.DataFrame(np.array([
['b1', 4, 9],
['b1', 5, 61],
['b1', 15, 95],
['b1', 24, 9],
['b2', 4, 5],
['b2', 6, 6],
['b2', 44, 0]]),
columns=['name', 'depth', 'attr1'])
df2 = pd.DataFrame(np.array([
['b1', 1, 6, 66],
['b1', 14, 16, 99],
['b1', 51, 55, 9],
['b3', 0, 5, 32]]),
columns=['name', 'top_depth', 'bottom_depth', 'attr2'])

om = pd.ordered_merge(df2, df1)
om = om.convert_objects(convert_numeric=True)
sandwiched = om.query('(depth > top_depth) & (depth <= bottom_depth)')

сэндвич:


  name  top_depth  bottom_depth  attr2  depth  attr1
0 b1 1 6 66 4 9
1 b1 1 6 66 5 61
6 b1 14 16 99 15 95

И я думаю, вы можете приложить остальные df1, используя join я не могу вспомнить.

В конце концов, это может быть не проблема в форме SQL. Можете ли вы предположить, что они отсортированы по глубине и top_depth? И перекрываются ли диапазоны df2? Итерация через каждый блок данных однажды может быть эффективным способом.

ответил(а) 2021-01-25T20:09:02+03:00 4 месяца, 3 недели назад
Ваш ответ
Введите минимум 50 символов
Чтобы , пожалуйста,
Выберите тему жалобы:

Другая проблема