Python pandas:根据多个字段的多个标准识别记录

Python pandas: Identify Records Based on Multiple Criteria on Multiple Fields

将 IPython (Python 3.4) 与 pandas 一起使用:我有一个大致如下所示的数据框(注意每个学生的重复记录,有时每个学生有 3+ 个):

Year    Subject   Student   Score   Date
2014    Math       1        34     31-Jan
2014    Math       1        34     26-Jan
2014    Math       2        65     26-Jan
2014    Math       2        76     31-Jan
2014    Math       3        45     3-Feb
2014    Math       3        67     31-Jan

我正在寻找一种方法来 return 基于以下标准的每个学生的分数: 1.最高分 当每个学生的记录分数相同时: 2. 最近日期

这是所需的输出:

Year    Subject   Student   Score   Date
2014    Math       1        34     31-Jan
2014    Math       2        76     31-Jan
2014    Math       3        67     31-Jan

这是我目前尝试过的方法: 在年份、学科和学生上使用 groupby 以获得给定年份和学科领域的每个学生的最高分数:

by_duplicate = df.groupby(['Year', 'Subject', 'Student'])
HighScore = by_duplicate[['Year', 'Subject', 'Student', 'Score']].max()

在这里,我重命名了 score 列,这样当我将它连接到原始数据框时,我就知道哪一列是哪一列。这可能不是必需的,但我不确定。

HighScore.rename(columns={'Score': 'Score2'}, inplace=True)

在这里,我添加了一个空白的 'HighScore' 列,以预期如果该行的得分最高,稍后将用 1 填充它。稍后会详细介绍...

HighScore['HighScore'] = ""

然后我为最近的日期做同样的事情:

Recent = by_duplicate[['Year', 'Subject', 'Student', 'Date']].max()
Recent.rename(columns={'Date': 'Date2'}, inplace=True)
Recent['Recent'] = ""

My approach was to 
1. create tables for each field (score and date) using groupby, 
2. identify the rows containing the highest and most recent scores, respectively, by entering a "1" in their respective new columns (HighScore' and 'Recent')
3. somehow join these grouped tables back to the original dataframe on Year, Subject, and Student
-I'm guessing this requires somehow ungrouping the groups as the pd.merge is not working on the grouped data frames
4. The end result, according to my theory, would look something like this:

Year    Subject   Student   Score   Date     HighScore  Recent
2014    Math       1        34     31-Jan    1          1   
2014    Math       1        34     26-Jan    1          0
2014    Math       2        65     26-Jan    0          0  
2014    Math       2        76     31-Jan    1          1  
2014    Math       3        45     3-Feb     0          1  
2014    Math       3        67     31-Jan    1          0

And once I have this table, I would need to do something like this:
1. Per student for a given year and subject area: return the sum of 'HighScore'
2. If the sum of 'HighScore' is greater than 1, then take the 'Recent' row equal to 1.
I believe this will give me what I need.

提前致谢!!!

如果我没看错的话,我想你可以通过对分数和日期进行排序来简化这个过程,这样每组的最后一个元素总是最高分中的最新元素。我可能会做类似

的事情
>>> df["FullDate"] = pd.to_datetime(df["Year"].astype(str) + "-" + df["Date"], 
                     format="%Y-%d-%b")
>>> df = df.sort(["Score", "FullDate"])
>>> df.groupby(["Year", "Subject", "Student"]).tail(1)
   Year Subject  Student  Score    Date   FullDate
0  2014    Math        1     34  31-Jan 2014-01-31
5  2014    Math        3     67  31-Jan 2014-01-31
3  2014    Math        2     76  31-Jan 2014-01-31

首先我创建了一个 FullDate 列,它是一个真正的日期时间而不是一个字符串,所以我知道它会正确排序。

请注意,我们排序的顺序很重要:我们希望首先按分数,然后在最大分数内 "largest"(最近的)日期最后。相反,如果我们换一种方式,我们就会有

>>> df = df.sort(["FullDate", "Score"]) # THIS IS THE WRONG ORDER
>>> df.groupby(["Year", "Subject", "Student"]).tail(1)
   Year Subject  Student  Score    Date   FullDate
0  2014    Math        1     34  31-Jan 2014-01-31
3  2014    Math        2     76  31-Jan 2014-01-31
4  2014    Math        3     45   3-Feb 2014-02-03

这会给我们最近一天的最高分。

现在确实排序是 ~O(N log N) 并且找到最大值可以在 O(N) 内完成,但恕我直言,简单性大大超过了通常较小的性能损失。