我之前曾问过类似的问题: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']
必须有一种更优雅的方式来做到这一点,所以我不会创建无用的列,让我的桌子不必要地大......
答案 0 :(得分:2)
您可以先使用dict
将a
,b
列与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