zzz*_*eek 6 python sql numpy pandas
给出两个数据帧如下:
>>> 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
Run Code Online (Sandbox Code Playgroud)
我们希望使用非简单的标准生成两种数据帧的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||
Run Code Online (Sandbox Code Playgroud)
我目前的内连接方法是通过向两者添加一列"1"来生成df_a和df_b的笛卡尔积,然后在"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
Run Code Online (Sandbox Code Playgroud)
对于外连接,我不确定最好的方法,到目前为止我一直在玩内部连接,然后应用标准的否定来获取所有其他行,然后尝试编辑"否定" "设置到原始,但它并没有真正起作用.
编辑.HYRY在这里回答了具体的问题,但是我需要更多通用的东西,更多的是在Pandas API中,因为我的连接标准可以是任何东西,而不仅仅是那个比较.对于outerjoin,首先我在"左"侧添加一个额外的索引,它将在我进行内连接后自行维护:
df_a['_left_index'] = df_a.index
Run Code Online (Sandbox Code Playgroud)
然后我们做笛卡尔并得到内连接:
cartesian = pd.merge(df_a, df_b, left_on='ones', right_on='ones')
innerjoin = cartesian[cartesian.c > cartesian.a]
Run Code Online (Sandbox Code Playgroud)
然后我在"df_a"中得到我们需要的额外索引ID,并从"df_a"获取行:
remaining_left_ids = set(df_a['_left_index']).\
difference(innerjoin['_left_index'])
remaining = df_a.ix[remaining_left_ids]
Run Code Online (Sandbox Code Playgroud)
然后我们使用一个直接的concat(),用左边的"NaN"替换缺少的列(我认为它之前没有这样做,但我想它确实如此):
outerjoin = pd.concat([innerjoin, remaining]).reset_index()
Run Code Online (Sandbox Code Playgroud)
我们想要对那些我们需要比较的cols做笛卡尔的想法基本上是正确的答案,尽管在我的具体情况下实现它可能有点棘手(一般化和全部).
问题:
你如何在"c> a"上产生df_1和df_2的"连接"?你会采用相同的"笛卡尔积,滤波器"方法还是有更好的方法?
你会如何产生相同的"左外连接"?
我使用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
Run Code Online (Sandbox Code Playgroud)
输出:
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
Run Code Online (Sandbox Code Playgroud)
内连接,因为这只计算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)
Run Code Online (Sandbox Code Playgroud)
输出:
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
Run Code Online (Sandbox Code Playgroud)
计算左外连接,用于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)
Run Code Online (Sandbox Code Playgroud)
输出:
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
Run Code Online (Sandbox Code Playgroud)