7

给定两个数据框,如下所示:

>>> import pandas as pd

>>> df_a = pd.DataFrame([{"a": 1, "b": 4}, {"a": 2, "b": 5}, {"a": 3, "b": 6}])
>>> df_b = pd.DataFrame([{"c": 2, "d": 7}, {"c": 3, "d": 8}])
>>> df_a
   a  b
0  1  4
1  2  5
2  3  6

>>> df_b
   c  d
0  2  7
1  3  8

我们想使用非简单标准生成两个数据帧的 SQL 样式连接,比如说“df_b.c > df_a.a”。据我所知,虽然merge()肯定是解决方案的一部分,但我不能直接使用它,因为它不接受“ON”标准的任意表达式(除非我遗漏了什么?)。

在 SQL 中,结果如下所示:

# inner join
sqlite> select * from df_a join df_b on c > a;
1|4|2|7
1|4|3|8
2|5|3|8

# outer join
sqlite> select * from df_a left outer join df_b on c > a;
1|4|2|7
1|4|3|8
2|5|3|8
3|6||

我目前的内部连接方法是生成 df_a 和 df_b 的笛卡尔积,方法是向两者添加一列“1”,然后在“1”列上使用 merge(),然后应用“c > a”标准。

>>> import numpy as np
>>> df_a['ones'] = np.ones(3)
>>> df_b['ones'] = np.ones(2)
>>> cartesian = pd.merge(df_a, df_b, left_on='ones', right_on='ones')
>>> cartesian
   a  b  ones  c  d
0  1  4     1  2  7
1  1  4     1  3  8
2  2  5     1  2  7
3  2  5     1  3  8
4  3  6     1  2  7
5  3  6     1  3  8
>>> cartesian[cartesian.c > cartesian.a]
   a  b  ones  c  d
0  1  4     1  2  7
1  1  4     1  3  8
3  2  5     1  3  8

对于外部联接,我不确定最好的方法,到目前为止,我一直在尝试获取内部联接,然后应用对标准的否定来获取所有其他行,然后尝试编辑该“否定” " 设置为原件,但它并没有真正起作用。

编辑. HYRY 在这里回答了具体问题,但我需要在 Pandas API 中更通用和更多的东西,因为我的加入标准可以是任何东西,而不仅仅是一个比较。对于外连接,首先我在“左侧”添加一个额外的索引,在我进行内连接后将保持自身:

df_a['_left_index'] = df_a.index

然后我们做笛卡尔并获得内部连接:

cartesian = pd.merge(df_a, df_b, left_on='ones', right_on='ones')
innerjoin = cartesian[cartesian.c > cartesian.a]

然后我在“df_a”中获得我们需要的额外索引 ID,并从“df_a”中获取行:

remaining_left_ids = set(df_a['_left_index']).\
                    difference(innerjoin['_left_index'])
remaining = df_a.ix[remaining_left_ids]

然后我们使用直接的 concat(),它将缺失的列替换为左侧的“NaN”(我认为它之前没有这样做,但我猜它确实如此):

outerjoin = pd.concat([innerjoin, remaining]).reset_index()

HYRY 仅在我们需要比较的那些列上进行笛卡尔计算的想法基本上是正确的答案,尽管在我的具体情况下,实现起来可能有点棘手(概括和全部)。

问题:

  1. 您将如何在“c > a”上生成 df_1 和 df_2 的“连接”?你会做同样的“笛卡尔积,过滤器”方法还是有更好的方法?

  2. 你将如何产生相同的“左外连接”?

4

3 回答 3

6

我使用 ufunc 的外部方法来计算结果,这里是例子:

首先,一些数据:

import pandas as pd
import numpy as np
df_a = pd.DataFrame([{"a": 1, "b": 4}, {"a": 2, "b": 5}, {"a": 3, "b": 6}, {"a": 4, "b": 8}, {"a": 1, "b": 7}])
df_b = pd.DataFrame([{"c": 2, "d": 7}, {"c": 3, "d": 8}, {"c": 2, "d": 10}])
print "df_a"
print df_a
print "df_b"
print df_b

输出:

df_a
   a  b
0  1  4
1  2  5
2  3  6
3  4  8
4  1  7
df_b
   c   d
0  2   7
1  3   8
2  2  10

内连接,因为这只计算c&的笛卡尔积a,内存使用量小于整个 DataFrame 的笛卡尔积:

ia, ib = np.where(np.less.outer(df_a.a, df_b.c))
print pd.concat((df_a.take(ia).reset_index(drop=True), 
                 df_b.take(ib).reset_index(drop=True)), axis=1)

输出:

   a  b  c   d
0  1  4  2   7
1  1  4  3   8
2  1  4  2  10
3  2  5  3   8
4  1  7  2   7
5  1  7  3   8
6  1  7  2  10

要计算左外连接,请使用查找不在内连接中的numpy.setdiff1d()所有行:df_a

na = np.setdiff1d(np.arange(len(df_a)), ia)
nb = -1 * np.ones_like(na)
oa = np.concatenate((ia, na))
ob = np.concatenate((ib, nb))
print pd.concat([df_a.take(oa).reset_index(drop=True), 
                 df_b.take(ob).reset_index(drop=True)], axis=1)

输出:

   a  b   c   d
0  1  4   2   7
1  1  4   3   8
2  1  4   2  10
3  2  5   3   8
4  1  7   2   7
5  1  7   3   8
6  1  7   2  10
7  3  6 NaN NaN
8  4  8 NaN NaN
于 2013-03-23T01:29:41.417 回答
1

这可以通过广播和 np.where 来完成。使用任何你想要的二元运算符来评估为真/假:

import operator as op

df_a = pd.DataFrame([{"a": 1, "b": 4}, {"a": 2, "b": 5}, {"a": 3, "b": 6}])
df_b = pd.DataFrame([{"c": 2, "d": 7}, {"c": 3, "d": 8}])

binOp   = op.lt
matches = np.where(binOp(df_a.a[:,None],df_b.c.values))

print pd.concat([df.ix[idxs].reset_index(drop=True) 
                 for df,idxs in zip([df_a,df_b],matches)],
                axis=1).to_csv()

,A B C D

0,1,4,2,7

1,1,4,3,8

2,2,5,3,8

于 2016-08-18T02:24:21.383 回答
0

来自pyjanitor的conditional_join可能有助于抽象/方便;该功能目前在开发中:

# pip install git+https://github.com/pyjanitor-devs/pyjanitor.git
import pandas as pd
import janitor

内部联接

 df_a.conditional_join(df_b, ('a', 'c', '<'))

  left    right
     a  b     c  d
0    1  4     2  7
1    1  4     3  8
2    2  5     3  8

左连接

df_a.conditional_join(df_b, ('a', 'c', '<'), how = 'left')

  left    right
     a  b     c    d
0    1  4   2.0  7.0
1    1  4   3.0  8.0
2    2  5   3.0  8.0
3    3  6   NaN  NaN

该函数采用元组的变量 (*args) 参数作为条件 ( col from left, col from_right, join operator)

于 2021-10-11T04:28:33.590 回答