为 pandas DataFrame 的每一行查找跨多列的模式
Find the mode across multiple columns for each row of a pandas DataFrame
我是 Python 和 Pandas 的新手。我想为 Opt_1 到 Opt_7 列的每一行找到最常见的项目。请注意,出于某种原因,一些空白单元格似乎有 NaN,而在其他情况下它有 None.
ID Col_1 Col_2 Opt_1 Opt_2 Opt_3 Opt_4 Opt_5 Opt_6 Opt_7
1 Game 1 Team 1 13
2 Game 1 Team 2 -13
3 Game 1 Team 1
4 Game 1 Team 2
5 Game 2 Team 1 -7.5 -7.5 -7.5 -7.5
6 Game 2 Team 2 7.5 7.5 7.5 7.5
7 Game 2 Team 1 -2.5 -1.5
8 Game 2 Team 2 2.5 1.5
9 Game 3 Team 1 3.5 3.5
10 Game 3 Team 2 -3.5 -3.5
11 Game 3 Team 1 -1 -1.5 -1
12 Game 3 Team 2 1 1.5 1
我已经尝试了以下代码,它对大多数行都按预期工作,但不是全部。而且有点慢。
def freq_value(series):
return Counter(series).most_common()[0][0]
for row in df.iterrows():
df['result'] = df.apply(lambda row: freq_value((row['Opt_1'], row['Opt_2'], row['Opt_3'], row['Opt_4'], row['Opt_5'], row['Opt_6'], row['Opt_7'])), axis=1)
以下是预期结果和实际结果:
ID Expected Actual Result
1 NaN NaN
2 NaN NaN
3 NaN NaN
4 NaN NaN
5 -7.5 -7.5
6 7.5 7.5
7 NaN NaN
8 NaN NaN
9 3.5 3.5
10 -3.5 -3.5
11 -1 NaN
12 1 NaN
有没有办法做到 100% 正确,而且可能不需要一次一行地遍历每一行?提前感谢您的任何建议。
>>> import numpy as np
>>> import pandas as pd
>>> df = pd.DataFrame({
... 'ID': range(1, 13),
... 'Col_1': [*(['Game 1'] * 4), *(['Game 2'] * 4), *(['Game 3'] * 4)],
... 'Col_2': ['Team 1', 'Team 2'] * 6,
... 'Opt_1': [13, -13, np.nan, np.nan, -7.5, 7.5, np.nan, np.nan, np.nan, np.nan, -1, 1],
... 'Opt_2': [np.nan, np.nan, np.nan, np.nan, -7.5, 7.5, -2.5, 2.5, 3.5, -3.5, -1.5, 1.5],
... 'Opt_3': [np.nan, np.nan, np.nan, np.nan, -7.5, 7.5, -1.5, 1.5, 3.5, -3.5, -1, 1],
... 'Opt_4': [np.nan, np.nan, np.nan, np.nan, -7.5, 7.5, np.nan, np.nan, np.nan, np.nan, np.nan, np.nan],
... 'Opt_5': [np.nan] * 12,
... 'Opt_6': [np.nan] * 12,
... 'Opt_7': [np.nan] * 12
... })
>>> df
ID Col_1 Col_2 Opt_1 Opt_2 Opt_3 Opt_4 Opt_5 Opt_6 Opt_7
0 1 Game 1 Team 1 13.0 NaN NaN NaN NaN NaN NaN
1 2 Game 1 Team 2 -13.0 NaN NaN NaN NaN NaN NaN
2 3 Game 1 Team 1 NaN NaN NaN NaN NaN NaN NaN
3 4 Game 1 Team 2 NaN NaN NaN NaN NaN NaN NaN
4 5 Game 2 Team 1 -7.5 -7.5 -7.5 -7.5 NaN NaN NaN
5 6 Game 2 Team 2 7.5 7.5 7.5 7.5 NaN NaN NaN
6 7 Game 2 Team 1 NaN -2.5 -1.5 NaN NaN NaN NaN
7 8 Game 2 Team 2 NaN 2.5 1.5 NaN NaN NaN NaN
8 9 Game 3 Team 1 NaN 3.5 3.5 NaN NaN NaN NaN
9 10 Game 3 Team 2 NaN -3.5 -3.5 NaN NaN NaN NaN
10 11 Game 3 Team 1 -1.0 -1.5 -1.0 NaN NaN NaN NaN
11 12 Game 3 Team 2 1.0 1.5 1.0 NaN NaN NaN NaN
>>> opts = ['Opt_{}'.format(i) for i in range(1, 8)]
>>> df[opts].mode(axis=1, dropna=False)
0
0 NaN
1 NaN
2 NaN
3 NaN
4 -7.5
5 7.5
6 NaN
7 NaN
8 NaN
9 NaN
10 NaN
11 NaN
使用 filter
到 select 列和 mode
+ mask
仅查找唯一模式:
(df.filter(like='Opt')
.mode(axis=1)
.set_axis(['a', 'b'], axis=1, inplace=False)
.eval('a.mask(b.notna())', engine='python'))
0 13.0
1 -13.0
2 NaN
3 NaN
4 -7.5
5 7.5
6 NaN
7 NaN
8 3.5
9 -3.5
10 -1.0
11 1.0
Name: a, dtype: float64
mode
将为给定行提供 return 所有 模式。这意味着如果有两个值出现频率相同,则输出中将有两列。上面的解决方案处理最多有两列的情况。
若众数唯一,解可简化为
df.filter(like='Opt').mode(axis=1).iloc[:, 0]
没有唯一模式时的另一种解决方案,这将推广到输出中的任意数量的列。
u = df.filter(like='Opt').mode(axis=1)
if len(u.columns) > 1:
u = u.iloc[:, 0].where(u.iloc[:, 1:].isna().all(axis=1))
u
0 13.0
1 -13.0
2 NaN
3 NaN
4 -7.5
5 7.5
6 NaN
7 NaN
8 3.5
9 -3.5
10 -1.0
11 1.0
Name: 0, dtype: float64
我是 Python 和 Pandas 的新手。我想为 Opt_1 到 Opt_7 列的每一行找到最常见的项目。请注意,出于某种原因,一些空白单元格似乎有 NaN,而在其他情况下它有 None.
ID Col_1 Col_2 Opt_1 Opt_2 Opt_3 Opt_4 Opt_5 Opt_6 Opt_7
1 Game 1 Team 1 13
2 Game 1 Team 2 -13
3 Game 1 Team 1
4 Game 1 Team 2
5 Game 2 Team 1 -7.5 -7.5 -7.5 -7.5
6 Game 2 Team 2 7.5 7.5 7.5 7.5
7 Game 2 Team 1 -2.5 -1.5
8 Game 2 Team 2 2.5 1.5
9 Game 3 Team 1 3.5 3.5
10 Game 3 Team 2 -3.5 -3.5
11 Game 3 Team 1 -1 -1.5 -1
12 Game 3 Team 2 1 1.5 1
我已经尝试了以下代码,它对大多数行都按预期工作,但不是全部。而且有点慢。
def freq_value(series):
return Counter(series).most_common()[0][0]
for row in df.iterrows():
df['result'] = df.apply(lambda row: freq_value((row['Opt_1'], row['Opt_2'], row['Opt_3'], row['Opt_4'], row['Opt_5'], row['Opt_6'], row['Opt_7'])), axis=1)
以下是预期结果和实际结果:
ID Expected Actual Result
1 NaN NaN
2 NaN NaN
3 NaN NaN
4 NaN NaN
5 -7.5 -7.5
6 7.5 7.5
7 NaN NaN
8 NaN NaN
9 3.5 3.5
10 -3.5 -3.5
11 -1 NaN
12 1 NaN
有没有办法做到 100% 正确,而且可能不需要一次一行地遍历每一行?提前感谢您的任何建议。
>>> import numpy as np
>>> import pandas as pd
>>> df = pd.DataFrame({
... 'ID': range(1, 13),
... 'Col_1': [*(['Game 1'] * 4), *(['Game 2'] * 4), *(['Game 3'] * 4)],
... 'Col_2': ['Team 1', 'Team 2'] * 6,
... 'Opt_1': [13, -13, np.nan, np.nan, -7.5, 7.5, np.nan, np.nan, np.nan, np.nan, -1, 1],
... 'Opt_2': [np.nan, np.nan, np.nan, np.nan, -7.5, 7.5, -2.5, 2.5, 3.5, -3.5, -1.5, 1.5],
... 'Opt_3': [np.nan, np.nan, np.nan, np.nan, -7.5, 7.5, -1.5, 1.5, 3.5, -3.5, -1, 1],
... 'Opt_4': [np.nan, np.nan, np.nan, np.nan, -7.5, 7.5, np.nan, np.nan, np.nan, np.nan, np.nan, np.nan],
... 'Opt_5': [np.nan] * 12,
... 'Opt_6': [np.nan] * 12,
... 'Opt_7': [np.nan] * 12
... })
>>> df
ID Col_1 Col_2 Opt_1 Opt_2 Opt_3 Opt_4 Opt_5 Opt_6 Opt_7
0 1 Game 1 Team 1 13.0 NaN NaN NaN NaN NaN NaN
1 2 Game 1 Team 2 -13.0 NaN NaN NaN NaN NaN NaN
2 3 Game 1 Team 1 NaN NaN NaN NaN NaN NaN NaN
3 4 Game 1 Team 2 NaN NaN NaN NaN NaN NaN NaN
4 5 Game 2 Team 1 -7.5 -7.5 -7.5 -7.5 NaN NaN NaN
5 6 Game 2 Team 2 7.5 7.5 7.5 7.5 NaN NaN NaN
6 7 Game 2 Team 1 NaN -2.5 -1.5 NaN NaN NaN NaN
7 8 Game 2 Team 2 NaN 2.5 1.5 NaN NaN NaN NaN
8 9 Game 3 Team 1 NaN 3.5 3.5 NaN NaN NaN NaN
9 10 Game 3 Team 2 NaN -3.5 -3.5 NaN NaN NaN NaN
10 11 Game 3 Team 1 -1.0 -1.5 -1.0 NaN NaN NaN NaN
11 12 Game 3 Team 2 1.0 1.5 1.0 NaN NaN NaN NaN
>>> opts = ['Opt_{}'.format(i) for i in range(1, 8)]
>>> df[opts].mode(axis=1, dropna=False)
0
0 NaN
1 NaN
2 NaN
3 NaN
4 -7.5
5 7.5
6 NaN
7 NaN
8 NaN
9 NaN
10 NaN
11 NaN
使用 filter
到 select 列和 mode
+ mask
仅查找唯一模式:
(df.filter(like='Opt')
.mode(axis=1)
.set_axis(['a', 'b'], axis=1, inplace=False)
.eval('a.mask(b.notna())', engine='python'))
0 13.0
1 -13.0
2 NaN
3 NaN
4 -7.5
5 7.5
6 NaN
7 NaN
8 3.5
9 -3.5
10 -1.0
11 1.0
Name: a, dtype: float64
mode
将为给定行提供 return 所有 模式。这意味着如果有两个值出现频率相同,则输出中将有两列。上面的解决方案处理最多有两列的情况。
若众数唯一,解可简化为
df.filter(like='Opt').mode(axis=1).iloc[:, 0]
没有唯一模式时的另一种解决方案,这将推广到输出中的任意数量的列。
u = df.filter(like='Opt').mode(axis=1)
if len(u.columns) > 1:
u = u.iloc[:, 0].where(u.iloc[:, 1:].isna().all(axis=1))
u
0 13.0
1 -13.0
2 NaN
3 NaN
4 -7.5
5 7.5
6 NaN
7 NaN
8 3.5
9 -3.5
10 -1.0
11 1.0
Name: 0, dtype: float64