该题目来源于力扣的pandas题库,链接如下:
——题目要求:
表:Users
+---------------+---------+ | Column Name | Type | +---------------+---------+ | id | int | | name | varchar | +---------------+---------+ id 是该表中具有唯一值的列。 name 是用户名字。
表:Rides
+---------------+---------+ | Column Name | Type | +---------------+---------+ | id | int | | user_id | int | | distance | int | +---------------+---------+ id 是该表中具有唯一值的列。 user_id 是本次行程的用户的 id, 而该用户此次行程距离为 distance 。
编写解决方案,报告每个用户的旅行距离。
返回的结果表单,以 travelled_distance
降序排列 ,如果有两个或者更多的用户旅行了相同的距离, 那么再以 name
升序排列 。
返回结果格式如下例所示。
示例 1:
输入: Users 表: +------+-----------+ | id | name | +------+-----------+ | 1 | Alice | | 2 | Bob | | 3 | Alex | | 4 | Donald | | 7 | Lee | | 13 | Jonathan | | 19 | Elvis | +------+-----------+ Rides 表: +------+----------+----------+ | id | user_id | distance | +------+----------+----------+ | 1 | 1 | 120 | | 2 | 2 | 317 | | 3 | 3 | 222 | | 4 | 7 | 100 | | 5 | 13 | 312 | | 6 | 19 | 50 | | 7 | 7 | 120 | | 8 | 19 | 400 | | 9 | 7 | 230 | +------+----------+----------+ 输出: +----------+--------------------+ | name | travelled_distance | +----------+--------------------+ | Elvis | 450 | | Lee | 450 | | Bob | 317 | | Jonathan | 312 | | Alex | 222 | | Alice | 120 | | Donald | 0 | +----------+--------------------+ 解释: Elvis 和 Lee 旅行了 450 英里,Elvis 是排名靠前的旅行者,因为他的名字在字母表上的排序比 Lee 更小。 Bob, Jonathan, Alex 和 Alice 只有一次行程,我们只按此次行程的全部距离对他们排序。 Donald 没有任何行程, 他的旅行距离为 0。
——思路流程:
首先将Rides表进行里程数的分组求和,然后进行左右合并,之后进行里程数和名字的升序排序。
——代码实现:
import pandas as pd
def top_travellers(users: pd.DataFrame, rides: pd.DataFrame) -> pd.DataFrame:
# 计算每个用户的旅行距离。提前进行排序,后续合并后数据变的复杂,再求和容易出错
rides= rides.groupby('user_id')['distance'].sum().reset_index()
# 将用户的旅行距离与用户信息合并。由于这里users的id对应rides的user_id,用左右连接。
# 填充空值防止后续排序报错
result_df = pd.merge(users, rides, left_on='id', right_on='user_id', how='left').fillna(0)
# 重命名列名
result_df.rename(columns={'distance': 'travelled_distance'}, inplace=True)
# 按 travelled_distance 降序排列 ,如果有两个或者更多的用户旅行了相同的距离, 那么再以 name 升序排列。在切片中,先排序第一个元素,当第一个元素排序完成后,再考虑第二个元素的排序。
result_df = result_df.sort_values(by=['travelled_distance', 'name'], ascending=[False, True])
return result_df[['name','travelled_distance']]
——关于.sum()和.transform('sum')后缀形式的区别:
---.sum()形式:
-
.sum()
:这是一个聚合函数,当应用在DataFrame的列上时,它会对整个列的数值进行求和,返回一个标量值(单个值)。如果你使用data.groupby('name')['distance'].sum()
,它会返回每个用户的旅行距离之和,而不会保留原始的行-用户映射关系。
---.transform('sum')形式:
- 这是一个转换函数,当应用在DataFrame的列上时,它会对每个分组的数值进行操作,并返回一个与原始DataFrame相同大小的序列。如果你使用
data.groupby('name')['distance'].transform('sum')
,它会返回一个包含每个用户的旅行距离之和的序列,并且会根据原始的行-用户映射关系进行填充。
---代码解释:
import pandas as pd
# 创建示例数据
data = {
'name': ['Alice', 'Bob', 'Alice', 'Bob', 'Alice'],
'distance': [10, 20, 30, 40, 50]
}
df = pd.DataFrame(data)
# 使用 .sum() 对整个列进行求和
total_distance_sum = df['distance'].sum()
print("Total distance sum using .sum():", total_distance_sum)
# 使用 .groupby().transform('sum') 对每个分组的数值进行求和
grouped_distance_sum = df.groupby('name')['distance'].transform('sum')
print("Grouped distance sum using transform('sum'):\n", grouped_distance_sum)
'''
Total distance sum using .sum(): 150
Grouped distance sum using transform('sum'):
0 90
1 60
2 90
3 60
4 90
Name: distance, dtype: int64
'''
可见单个.sum返回一个标量,而transform返回点对点的聚合数据。