根据列值之间的差距对数据框进行分组
Grouping dataframe based on a gap between column values
假设我们有以下数据集:
Name Num
First 1
Second 50
Third 110
Fourth 2
Fifth 58
Sixth 105
Seventh 8
我想根据 num 列的每一行之间的最大间隙(例如)10 对数据帧进行分组。因此,例如,我想要:
Name Num
First,Fourth,Seventh 1,2,8
Second,Fifth 50,58
Sixth,Third 105,110
我尝试使用 Raymond Hettinger's Answer,但我需要对数据框进行分组,而不仅仅是列,因为我想在之后做一个条形图。
我试过这个(我首先根据列对数据框进行排序):
for idx,val in enumerate(df_sorted['num'], start=1):
if idx == 1:
groups =[df_sorted.iloc[idx]]
elif abs(data['pos'][idx] - data['pos'][idx-1]) <= 10:
groups[-1].append(data.iloc[idx,:])
else:
groups.append(data.iloc[idx,:])
但是我在理解列表在 Python 中的工作方式时遇到了一些问题(我一直在 R 中使用它们)。
不知道是否有更简单的方法使用 groupby 或其他方法(例如 Grouper 用于时间序列)。
编辑:
最后,我选择了以下方法来解决问题,使用 Raymond Hettinger 的答案:
def cluster(data, maxgap, column):
'''Arrange data into groups where successive elements
differ by no more than *maxgap*
'''
#data.sort() #sort data if necessary
groups = list()
groups = [[data.iloc[0,:]]]
for idx,val in enumerate(data[column]):
if idx > 0:
if abs(val - data[column][idx-1]) <= maxgap:
groups[-1].append(data.iloc[idx,:])
else:
groups.append([data.iloc[idx,:]])
return groups
我们将数字分箱以计算分布,然后根据该分布对它们进行分组和列出; N=24 是有意根据您的预期结果量身定制的。实际操作的阈值可能更难设置。例如,对于每 10 个类别,50 和 58 将是不同的组。
import pandas as pd
import numpy as np
import io
data = '''
Name Num
First 1
Second 50
Third 110
Fourth 2
Fifth 58
Sixth 105
Seventh 8
'''
df = pd.read_csv(io.StringIO(data), sep='\s+')
df.sort_values('Num', ascending=True, inplace=True, ignore_index=True)
N = 24
category = pd.cut(df['Num'], bins=np.arange(df['Num'].min(), df['Num'].max()+ N, N), right=False)
category.value_counts()
[1, 25) 3
[97, 121) 2
[49, 73) 2
[73, 97) 0
[25, 49) 0
Name: Num, dtype: int64
df.groupby(category).agg(list)
Name Num
Num
[1, 25) [First, Fourth, Seventh] [1, 2, 8]
[25, 49) [] []
[49, 73) [Second, Fifth] [50, 58]
[73, 97) [] []
[97, 121) [Sixth, Third] [105, 110]
假设我们有以下数据集:
Name Num
First 1
Second 50
Third 110
Fourth 2
Fifth 58
Sixth 105
Seventh 8
我想根据 num 列的每一行之间的最大间隙(例如)10 对数据帧进行分组。因此,例如,我想要:
Name Num
First,Fourth,Seventh 1,2,8
Second,Fifth 50,58
Sixth,Third 105,110
我尝试使用 Raymond Hettinger's Answer,但我需要对数据框进行分组,而不仅仅是列,因为我想在之后做一个条形图。 我试过这个(我首先根据列对数据框进行排序):
for idx,val in enumerate(df_sorted['num'], start=1):
if idx == 1:
groups =[df_sorted.iloc[idx]]
elif abs(data['pos'][idx] - data['pos'][idx-1]) <= 10:
groups[-1].append(data.iloc[idx,:])
else:
groups.append(data.iloc[idx,:])
但是我在理解列表在 Python 中的工作方式时遇到了一些问题(我一直在 R 中使用它们)。 不知道是否有更简单的方法使用 groupby 或其他方法(例如 Grouper 用于时间序列)。
编辑: 最后,我选择了以下方法来解决问题,使用 Raymond Hettinger 的答案:
def cluster(data, maxgap, column):
'''Arrange data into groups where successive elements
differ by no more than *maxgap*
'''
#data.sort() #sort data if necessary
groups = list()
groups = [[data.iloc[0,:]]]
for idx,val in enumerate(data[column]):
if idx > 0:
if abs(val - data[column][idx-1]) <= maxgap:
groups[-1].append(data.iloc[idx,:])
else:
groups.append([data.iloc[idx,:]])
return groups
我们将数字分箱以计算分布,然后根据该分布对它们进行分组和列出; N=24 是有意根据您的预期结果量身定制的。实际操作的阈值可能更难设置。例如,对于每 10 个类别,50 和 58 将是不同的组。
import pandas as pd
import numpy as np
import io
data = '''
Name Num
First 1
Second 50
Third 110
Fourth 2
Fifth 58
Sixth 105
Seventh 8
'''
df = pd.read_csv(io.StringIO(data), sep='\s+')
df.sort_values('Num', ascending=True, inplace=True, ignore_index=True)
N = 24
category = pd.cut(df['Num'], bins=np.arange(df['Num'].min(), df['Num'].max()+ N, N), right=False)
category.value_counts()
[1, 25) 3
[97, 121) 2
[49, 73) 2
[73, 97) 0
[25, 49) 0
Name: Num, dtype: int64
df.groupby(category).agg(list)
Name Num
Num
[1, 25) [First, Fourth, Seventh] [1, 2, 8]
[25, 49) [] []
[49, 73) [Second, Fifth] [50, 58]
[73, 97) [] []
[97, 121) [Sixth, Third] [105, 110]