SQL запрос присоединиться к Pandas - PullRequest
2 голосов
/ 16 февраля 2020

Я хотел бы объединить две таблицы в Pandas.

df_types Содержит размер диапазона типа продукта (5000 строк)

| Table: TYPES |          |      |
|--------------|----------|------|
| size_max     | size_min | type |
| 1            | 5        | S    |
| 6            | 16       | M    |
| 16           | 24       | L    |
| 25           | 50       | XL   |

Код кадра данных в Pandas:

df_types = pd.DataFrame([[1,5,'S'],
                         [6,16,'M'],
                         [16,24,'L'],
                         [25,50,'XL']],
                        columns = ['size_min','size_max','type'])

df_products Содержит идентификатор продукта и размер (12000 строк)

| Table: Products |      |
|-----------------|------|
| id_product      | size |
| A               | 6    |
| B               | 25   |
| C               | 7    |
| D               | 2    |
| F               | 45   |
| E               | 10   |
| G               | 16   |

Код кадра данных в Pandas:

df_products = pd.DataFrame([['A',6,],
                            ['B',25],
                            ['C',7],
                            ['D',2],
                            ['F',45],
                            ['E',10],
                            ['G',16]],columns = ['id_product','size'])

Я хотел бы сделать это SQL присоединиться к Pandas:

SELECT  *.df_products
        type.df_types
FROM    df_products     LEFT JOIN df_types
                        ON  df_products.size >= df_types.size_min
                            AND df_products.size <= df_types.size_max

РЕЗУЛЬТАТ:

| id_product | size | type |
|------------|------|------|
| A          | 6    | M    |
| B          | 25   | XL   |
| C          | 7    | M    |
| D          | 2    | S    |
| F          | 45   | XL   |
| E          | 10   | M    |
| G          | 16   | M    |

спасибо! ; -)

Ответы [ 2 ]

2 голосов
/ 16 февраля 2020

Метод 1: outer join с pd.merge

Хотя это обычная операция SQL, для этого просто не существует простого метода с pandas.

Одно из решений здесь было бы сделать outer join для сопоставления всех строк, а затем использовать DataFrame.query фильтровать строки, где size находится между size_min & size_max.

Но это приводит к взрыву строк так что в вашем случае 12000*5000 = 60 000 000 строк.

dfn = (
    df_products.assign(key=1)
      .merge(df_types.assign(key=1), on='key')
      .query('size >= size_min & size < size_max')
      .drop(columns='key')
)

   id_product  size  size_min  size_max type
1           A     6         6        16    M
7           B    25        25        50   XL
9           C     7         6        16    M
12          D     2         1         5    S
19          F    45        25        50   XL
21          E    10         6        16    M
26          G    16        16        24    L

Способ 2: pd.IntervalIndex:

Если у вас нет перекрывающихся диапазонов, поэтому если мы изменим size_min 16 в кадре данных df_types до 15, мы можем использовать этот метод. Это не приведет к взрыву рядов.

idx = pd.IntervalIndex.from_arrays(df_types['size_min'], df_types['size_max'], closed='both')
event = df_types.loc[idx.get_indexer(df_products['size']), 'type'].to_numpy()

df_products['type'] = event

  id_product  size type
0          A     6    M
1          B    25   XL
2          C     7    M
3          D     2    S
4          F    45   XL
5          E    10    M
6          G    16    L
0 голосов
/ 16 февраля 2020

Дольше, чем решение Эрфана; Я просто считаю, что это может помочь избежать увеличения числа строк в результате слияния. То, что это делает, ищет cond1 и cond2, которые соответствуют предложению where в запросе sql. На следующем шаге происходит сжатие обоих списков и поиск индекса элемента (True, True) ... полученный индекс является эквивалентом индекса для df_types. объединить все кадры данных, извлеченные из df_types на основе индексов, и снова выполнить конкататацию в df_products. Должно быть лучше, чем это; Однако я верю, что SQL делает это лучше.

cond1 = df_products['size'].apply(lambda x: [x>=i for i in [*df_types.size_min.array]])

cond2 = df_products['size'].apply(lambda x: [x<i for i in [*df_types.size_max.array]])

t = [list(zip(i,j)).index((True,True))
     for i,j in zip(cond1.array,cond2.array)]

result = (pd.concat([df_types.iloc[[i]]
                     for i in t])
          .filter(['type'])
          .reset_index(drop=True))

outcome = (pd.concat([df_products,result],
           axis=1,
           ignore_index=True,
           join='outer'))

outcome.columns = ['id_product', 'size', 'type']

    id_product  size    type
0   A   6   M
1   B   25  XL
2   C   7   M
3   D   2   S
4   F   45  XL
5   E   10  M
6   G   16  L

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

from itertools import product
test = [(id_product,first,last)
        for (id_product,first), (second, third,last)
        in product(zip(df_products.id_product,df_products['size']),
                   df_types.to_numpy()
                  )
        if second <= first <= third
       ]

test

[('A', 6, 'M'),
 ('B', 25, 'XL'),
 ('C', 7, 'M'),
 ('D', 2, 'S'),
 ('F', 45, 'XL'),
 ('E', 10, 'M'),
 ('G', 16, 'M'),
 ('G', 16, 'L')]

, чтобы получить pandas фрейм данных:

pd.DataFrame(test,columns=['id_product', 'size', 'type'])
    id_product  size    type
0      A         6       M
1      B        25       XL
2      C        7        M
3      D        2        S
4      F        45       XL
5      E        10       M
6      G        16       M
7      G        16       L

обратите внимание, что последний элемент 'G' возвращает две строки, так как он соответствует тем, в зависимости от условий.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...