【问题标题】:SQL query join in PandasPandas 中的 SQL 查询连接
【发布时间】:2020-02-16 12:14:07
【问题描述】:

我想在 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 包含产品 id 和大小(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    |

谢谢! ;-)

【问题讨论】:

  • 您的桌子有多大?行数
  • df_types 5000 行和 df_products 12000 行

标签: python pandas dataframe join


【解决方案1】:

方法一:outer joinpd.merge

虽然这是SQL 的常见操作,但pandas 没有直接的方法。

这里的解决方案之一是使用outer join 匹配所有行,然后使用DataFrame.query 过滤size 介于size_minsize_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

方法二:pd.IntervalIndex

如果你没有重叠的范围,那么如果我们将数据框df_types中的size_min 16更改为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

【讨论】:

    【解决方案2】:

    比二凡的解决方案要长得多;只是提供这个因为我相信它可以帮助避免合并导致的行数增加。这样做是在 sql 查询中查找与 where 子句匹配的 cond1 和 cond2。下一步压缩两个列表并找到元素的索引 (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
    

    更新:时间流逝,希望我们变得更好。又试了一次,但在将最终结果返回给 Pandas 之前将交易转移到 vanilla python 中:

    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')]
    

    获取熊猫数据框:

    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”返回两行,因为它根据条件匹配这两行。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-04-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-10-07
      • 1970-01-01
      相关资源
      最近更新 更多