Pandas条件列计数(复杂映射)

时间:2017-01-04 06:48:17

标签: python pandas numpy conditional

我之前曾问过类似的问题:Pandas Conditional Column Count

但是修改了它:

我有一个如下所示的数据框:

a1   |  a2  |  b3  |  b4  |  b5  |  c | d1 | d2 | d3 | d4 | d5
 1   |   2  |   3  |  4   |   5  |  1 | 1  | 0  |  0 |  0 | 0
 1   |   4  |   5  |  3   |   2  |  0 | 0  | 1  |  1 |  1 | 0
 2   |   3  |   1  |  1   |   0  |  0 | 0  | 0  |  1 |  0 | 1

我想创建两个列," a_count"和" b_count"。

基本上,d1-d5中的布尔标志对应于a1 / a2 / b3 / b4 / b5。即在第一行中,对于d1,' 1'对应于a1下第一行的实体。

我可以假设d1-d5的列将与a1 / a2 / b3 / b4 / b5中的列均匀匹配(即总数#将相等,保证每个cols来自a或b将有相应的“d”列。

我想做一些类似于我原来问题的事情,我有" a_count"和" b_count"列,但条件略有不同。以前,我只有一栏' d'为了一切。因为我现在将它拆分为与特定列对应...

'一个' count表示以' a'开头的列的次数。 相应' d' column == 1,而整个行的c == 0。例如:

a1   |  a2  |  b3  |  b4  |  b5  |  c | d1 | d2 | d3 | d4 | d5 | a_count | b_count 
 1   |   2  |   3  |  4   |   5  |  1 | 1  | 0  |  0 |  0 | 0  |    0    |   0
 1   |   4  |   5  |  3   |   2  |  0 | 0  | 1  |  1 |  1 | 0  |    1    |   0 
 2   |   3  |   1  |  1   |   0  |  0 | 1  | 1  |  1 |  0 | 1  |    0    |   1

我最初想过使用这样的东西(用户在原始帖子中评论过):

anyone = df[['c', 'd']].eq(1).any(1)
df['a_count'] = df.filter(like='a').eq(1).sum(1) * anyone
df['b_count'] = df.filter(like='b').eq(1).sum(1) * anyone

但问题在于我无法保证a1 / d1,a2 / d2,b3 / d3,b4 / d4,b5 / d5之间的匹配,如果我只是在任意位置任意加总它们#39; s等于1 ...(如果这是有道理的)。

我的直觉是使用一个非常复杂的np.where语句(见下文)。我不知道是否有更优雅的方式来做到这一点,但是......

df['z1'] = np.where(((df['c'] == 0) & (df['a1'] == 1) & (df['d1'] == 1), 1, 0)
df['z2'] = np.where(((df['c'] == 0) & (df['a2'] == 1) & (df['d2'] == 1), 1, 0)
df['z3'] = np.where(((df['c'] == 0) & (df['b3'] == 1) & (df['d3'] == 1), 1, 0)
df['z4'] = np.where(((df['c'] == 0) & (df['b4'] == 1) & (df['d4'] == 1), 1, 0)
df['z5'] = np.where(((df['c'] == 0) & (df['b5'] == 1) & (df['d5'] == 1), 1, 0)

然后,

df['a_count'] = df['z1'] + df['z2']
df['b_count'] = df['z3'] + df['z4'] + df['z5']

必须有一种更优雅的方式来做到这一点,所以我不会创建无用的列,让我的桌子不必要地大......

1 个答案:

答案 0 :(得分:2)

您可以先使用dictab列与d进行映射:

d = {'a1':'d1','a2':'d2','b4':'d4','b5':'d5','b3':'d3'}

然后按mul多个掩码(转换为int是必要的,以避免warning),最后filter需要sum

df1 = (df[list(d.keys())] == 1).mul((df[list(d.values())] == 1).astype(int).values, axis=0)
                               .mul(df.c == 0, axis=0)
print (df1)
   a2  b5  b4  b3  a1
0   0   0   0   0   0
1   0   0   0   0   0
2   0   0   0   1   0

df['a_count'] = df1.filter(like='a').sum(axis=1)
df['b_count'] = df1.filter(like='b').sum(axis=1)
print (df)
   a1  a2  b3  b4  b5  c  d1  d2  d3  d4  d5  a_count  b_count
0   1   2   3   4   5  1   1   0   0   0   0        0        0
1   1   4   5   3   2  0   0   1   1   1   0        0        0
2   2   3   1   1   0  0   0   0   1   0   1        0        1

使用MultiIndex的另一个更动态但更复杂的解决方案:

#keep original df
df2 = df.copy()
#set index with columns not matches
df = df.set_index('c')
#create Multiindex with ints and strings
a = df.columns.str.extract('(\d+)', expand=False).astype(int)
b = df.columns.str.extract('([A-Za-z]+)', expand=False)
mux = pd.MultiIndex.from_arrays([a,b])
df.columns = mux
#SORT INDEX FOR ALIGN ab dataframe with d
df = df.sort_index(axis=1)
print (df)
   1     2     3     4     5   
   a  d  a  d  b  d  b  d  b  d
c                              
1  1  1  2  0  3  0  4  0  5  0
0  1  0  4  1  5  1  3  1  2  0
0  2  0  3  0  1  1  1  0  0  1
#select columns with a,b
idx = pd.IndexSlice
ab = df.loc[:, idx[:, ['a','b']]]
print (ab)
   1  2  3  4  5
   a  a  b  b  b
c               
1  1  2  3  4  5
0  1  4  5  3  2
0  2  3  1  1  0

#select columns with d
d = df.loc[:, idx[:, 'd']]
print (d)
   1  2  3  4  5
   d  d  d  d  d
c               
1  1  0  0  0  0
0  0  1  1  1  0
0  0  0  1  0  1
#multiple masks
df1 = (ab == 1).mul((d == 1).astype(int).values, axis=0)
               .mul(df.index == 0, axis=0)
               .reset_index(drop=True)
print (df1)
   1  2  3  4  5
   a  a  b  b  b
0  0  0  0  0  0
1  0  0  0  0  0
2  0  0  1  0  0

#select columns with a and b
df2['a_count'] =  df1.loc[:, idx[:, 'a']].sum(axis=1)
df2['b_count'] =  df1.loc[:, idx[:, 'b']].sum(axis=1)
print (df2)
   a1  a2  b3  b4  b5  c  d1  d2  d3  d4  d5  a_count  b_count
0   1   2   3   4   5  1   1   0   0   0   0        0        0
1   1   4   5   3   2  0   0   1   1   1   0        0        0
2   2   3   1   1   0  0   0   0   1   0   1        0        1