如何根据已从一个类别转移到另一个类别(按顺序)的 id 列计算行数?

How to count the numbers of rows based on an id column that have shifted from one category to another (in order)?

我有一个这样的 pandas 数据框:

         id    year_group  category
0        8300           1     low
1        8300           2     medium
2       11725           1     low
3       11725           2     low
4       18068           1     medium
...       ...         ...       ...
59354   18962           1     low
59355   11669           1     low
59356   13110           3     low
59357    2378           1     low
59358   19363           1     low

[59359 rows x 3 columns]

我正在尝试根据一年(year_group 列)确定有多少 ID 从一个类别转移到另一个类别。例如,对于 id 8300,​​从第 1 年到第 2 年的转变表明类别从低到中(按此顺序)的变化。我想计算每个类别在 year_group 秒 1 到 3 之间发生这种情况的次数。

我不确定如何完成此操作。目前,我考虑过在尝试对所有内容进行分组之前删除每个 year_group 并制作单独的数据框,如下所示:

# year 1 and 2 
years_1_2  = df.drop(df[df.year_group == 3].index)

但是,我不知道如何确保分组是基于 year_group 的位置,即第 1 年到第 2 年而不是第 2 年到第 1 年。

也许我可以做一些更精简的事情。也许利用 np.where... 关于如何最好地解决这个问题有什么建议吗?

也许这可以帮助您入门。我相信这有点冗长,但很清楚。 在线评论

# added and modified data for just 2 years
data='''         id    year_group  category
0        8300           1     low
1        8300           2     medium
2       11725           1     low
3       11725           2     low
4       18068           1     medium
5       18068           2     low
6   18962           1     low
7   18962           2     low
8        21           1     low
9        21           2     medium'''
df = pd.read_csv(io.StringIO(data), sep=' \s+', engine='python')

# sort to keep ids and year_groups ascending
df.sort_values(['id', 'year_group'], ascending=[True, True], inplace=True)

      id  year_group category
8     21           1      low
9     21           2   medium
0   8300           1      low
1   8300           2   medium
2  11725           1      low
3  11725           2      low
4  18068           1   medium
5  18068           2      low
6  18962           1      low
7  18962           2      low

# if you have year 3, this will only take years 1 and 2
# if a grouping has a count of 2, that means there is no change from one year to the next, so drop everything that didn't change
dft = df[df[df['year_group'] != 3].groupby(['id', 'category'])['year_group'].transform('count') < 2]

      id  year_group category
8     21           1      low
9     21           2   medium
0   8300           1      low
1   8300           2   medium
4  18068           1   medium
5  18068           2      low

# making lists that show movement from low - medium, medium - low, etc...
yearlychanges = dft.groupby('id')['category'].apply(list).reset_index()

      id       category
0     21  [low, medium]
1   8300  [low, medium]
2  18068  [medium, low]

# convert lists to strings for counting
yearlychanges['changes'] = yearlychanges.apply(lambda x: '-'.join(x['category']), axis=1)

      id       category     changes
0     21  [low, medium]  low-medium
1   8300  [low, medium]  low-medium
2  18068  [medium, low]  medium-low

# count number of changes
yearlychanges[['changes', 'id']].groupby('changes').count()

            id
changes
low-medium   2
medium-low   1

如果我理解正确的话:

Setup

df = pd.DataFrame(data={{'id': [8300, 8300, 8300, 8301, 8301, 8301], 'year_group': [1, 2, 3, 1, 2, 3], 'category': ['low', 'medium', 'low', 'low', 'medium', 'low']}})

Code

df['shift'] = df.groupby('id')['category'].apply(lambda x: x + ' ' + x.shift(-1))
shifts = df.dropna(subset=['shift']).groupby(['shift']).size()

Input

         id  year_group category
0      8300           1      low
1      8300           2   medium
2      8300           3      low
59355  8301           1      low
59356  8301           2   medium
59357  8301           3      low

Output:

shift
low medium    2
medium low    2