2016-11-10 58 views
3

这是一个奇怪的例子:我有3个数据框,“prov_data”包含提供者ID并计入区域和类别(即,提供程序与这些区域和类别交互的次数) 。尝试合并具有多种条件的数据框

prov_data = DataFrame({'aprov_id':[1122,3344,5566,7788],'prov_region_1':[0,0,4,0],'prov_region_2':[2,0,0,0], 
        'prov_region_3':[0,1,0,1],'prov_cat_1':[0,2,0,0],'prov_cat_2':[1,0,3,0],'prov_cat_3':[0,0,0,4], 
        'prov_cat_4':[0,3,0,0]}) 

enter image description here

“tender_data”,其中包含相同的,但招标。

tender_data = DataFrame({'atender_id':['AA12','BB33','CC45'], 
        'ten_region_1':[0,0,1,],'ten_region_2':[0,1,0], 
        'ten_region_3':[1,1,0],'ten_cat_1':[1,0,0], 
        'ten_cat_2':[0,1,0],'ten_cat_3':[0,1,0], 
        'ten_cat_4':[0,0,1]}) 

enter image description here

最后一个 “NO_MATCH” DF至极包含供应商,招标之间禁止匹配。

no_match = DataFrame({ 'prov_id':[1122,3344,5566], 
      'tender_id':['AA12','BB33','CC45']}) 

enter image description here

我需要做到以下几点:创建一个新的DF,将追加prov_data & tender_data DataFrames的行,如果他们(1)匹配的一个或多个类别(即在同一类别> 0)AND(2)匹配一个或多个区域AND(3)不在no_match列表中。

所以这会给我这个DF:

df = DataFrame({'aprov_id':[1122,3344,7788],'prov_region_1':[0,0,0],'prov_region_2':[2,0,0], 
        'prov_region_3':[0,1,1],'prov_cat_1':[0,2,0],'prov_cat_2':[1,0,0],'prov_cat_3':[0,0,4], 
        'prov_cat_4':[0,3,0], 'atender_id':['BB33','AA12','BB33'], 
        'ten_region_1':[0,0,0],'ten_region_2':[1,0,1], 
        'ten_region_3':[1,1,1],'ten_cat_1':[0,1,0], 
        'ten_cat_2':[1,0,1],'ten_cat_3':[1,0,1], 
        'ten_cat_4':[0,0,0]}) 

回答

2

代码

# the first columns of each dataframe are the ids 
# i'm going to use them several times 
tid = tender_data.values[:, 0] 
pid = prov_data.values[:, 0] 
# first columns [1, 2, 3, 4] are cat columns 
# we could have used filter, but this is good 
# for this example 
pc = prov_data.values[:, 1:5] 
tc = tender_data.values[:, 1:5] 
# columns [5, 6, 7] are rgn columns 
pr = prov_data.values[:, 5:] 
tr = tender_data.values[:, 5:] 

# I want to mave this an m x n array, where 
# m = number of rows in prov df and n = rows in tender 
nm = no_match.groupby(['prov_id', 'tender_id']).size().unstack() 
nm = nm.reindex_axis(tid, 1).reindex_axis(pid, 0) 
nm = ~nm.fillna(0).astype(bool).values * 1 

# the dot products of the cat arrays gets a handy 
# array where there are > 1 co-positive values 
# this combined with the a no_match construct 
a = pd.DataFrame(pc.dot(tc.T) * pr.dot(tr.T) * nm > 0, pid, tid) 
a = a.mask(~a).stack().index 

fp = a.get_level_values(0) 
ft = a.get_level_values(1) 

pd.concat([ 
     prov_data.set_index('aprov_id').loc[fp].reset_index(), 
     tender_data.set_index('atender_id').loc[ft].reset_index() 
    ], axis=1) 


    index prov_cat_1 prov_cat_2 prov_cat_3 prov_cat_4 prov_region_1 \ 
0 1122   0   1   0   0    0 
1 3344   2   0   0   3    0 
2 7788   0   0   4   0    0 

    prov_region_2 prov_region_3 atender_id ten_cat_1 ten_cat_2 ten_cat_3 \ 
0    2    0  BB33   0   1   1 
1    0    1  AA12   1   0   0 
2    0    1  BB33   0   1   1 

    ten_cat_4 ten_region_1 ten_region_2 ten_region_3 
0   0    0    1    1 
1   0    0    0    1 
2   0    0    1    1 

解释

  • 使用点产品来确定比赛
  • 许多其他的事情我会尽力解释,只使用“标准” pandas技术更晚
+0

谢谢!这工作完美,原来的dfs是600K x 75,所以这是一个非常好的选择。 – castor

+0

我在运行原始数据框后,实际上得到了退出代码137。我认为主要的问题是no_match DF,它有600K行。供应商矩阵有43K和投标矩阵140K ...任何想法@piRSquared? – castor

+0

http:// stackoverflow。com/a/1041309/2336654 如果该链接是正确的,我猜你正在服务器上运行一个管理员杀死你的脚本。 – piRSquared

0

简单的解决方案。

prov_data['tkey'] = 1 
tender_data['tkey'] = 1 
df1 = pd.merge(prov_data,tender_data,how='outer',on='tkey') 
df1 = pd.merge(df1,no_match,how='outer',left_on = 'aprov_id', right_on = 'prov_id') 
df1['dropData'] = df1.apply(lambda x: True if x['tender_id'] == x['atender_id'] else False, axis=1) 
df1['dropData'] = df1.apply(lambda x: (x['dropData'] == True) or not(
            ((x['prov_cat_1'] > 0 and x['ten_cat_1'] > 0) or 
             (x['prov_cat_2'] > 0 and x['ten_cat_2'] > 0) or 
             (x['prov_cat_3'] > 0 and x['ten_cat_3'] > 0) or 
             (x['prov_cat_4'] > 0 and x['ten_cat_4'] > 0)) and(
             (x['prov_region_1'] > 0 and x['ten_region_1'] > 0) or 
             (x['prov_region_2'] > 0 and x['ten_region_2'] > 0) or 
             (x['prov_region_3'] > 0 and x['ten_region_3'] > 0))),axis=1) 
df1 = df1[~df1.dropData] 
df1 = df1[[u'aprov_id', u'atender_id', u'prov_cat_1', u'prov_cat_2', u'prov_cat_3', 
      u'prov_cat_4', u'prov_region_1', u'prov_region_2', u'prov_region_3', 
      u'ten_cat_1', u'ten_cat_2', u'ten_cat_3', u'ten_cat_4', u'ten_region_1', 
      u'ten_region_2', u'ten_region_3']].reset_index(drop=True) 

print df1.equals(df) 

首先我们两者都做dataframes的全交叉产品和合并,与no_match数据框中,然后添加一个布尔值列,以纪念所有行被丢弃。

布尔列由两个带有所有必要条件的布尔lambda函数分配,然后我们只取其中那列的所有行为False

由于合并操作,此解决方案对资源不是非常友好,所以如果您的数据非常大,则可能是不利的。

+0

谢谢!是的,我原来的DF有600K行和76列,所以第一个选项更可行。就像你说的,他是一个很好的直接选择。 – castor

相关问题