我有一个DataFrame,其中包含数千个汽车的列表。每辆车都有一个开始年份列和一个结束年份列,表示汽车流通的年份间隔。每辆车在整个时期内都有一个平均油耗列,如下:
df_cars
+-----+------------+----------+--------------------------+
| Car | Start year | End year | Average fuel consumption |
+-----+------------+----------+--------------------------+
| 1 | 2002 | 2025 | 10.0 |
+-----+------------+----------+--------------------------+
| 2 | 1995 | 2008 | 12.5 |
+-----+------------+----------+--------------------------+
| 3 | 2005 | 2017 | 8.5 |
+-----+------------+----------+--------------------------+
在2000年至2015年期间,我想获得每年运营的车队的平均油耗" Y"。因此,如果汽车具有开始年份< Y和结束年份>是的,它应该包含在该年的船队平均数中。显然,大多数汽车将出现在车队平均数年内。
截至目前,我正在进行循环,但速度相当慢。
for y in range(2000, 2015):
df_cars[(df_cars["Start year"]<=int(y))&(df_cars["End year"]>=int(y))]["Average fuel consumption"].mean(axis=0)
有更快的方法吗? 谢谢。
答案 0 :(得分:1)
以下是使用collections.Counter
的一种方式。
它不矢量化,但似乎仍然提供了13倍的性能提升。
有一个pandonic但仍然是循环的替代here,但我找不到矢量化实现。
import pandas as pd
from collections import Counter
df = pd.DataFrame([[1, 2002, 2025, 10.0],
[2, 1995, 2008, 12.5],
[3, 2005, 2017, 8.5]],
columns=['Car', 'StartYear', 'EndYear', 'AvgFuelConsumption'])
def jp(df):
# first get range of years
year_range = range(df['StartYear'].min(), df['EndYear'].max()+1)
res = pd.DataFrame(index=year_range, columns=['AvgFuelConsumption'])
# use collections.Counter for sums and counts
c_sum = Counter()
c_count = Counter()
for idx, car, start, end, fuel in df.itertuples():
for i in range(start, end+1):
c_sum[i] += fuel
c_count[i] += 1
# calculate averages by year
c_res = {y: c_sum[y] / c_count[y] for y in c_sum}
# create dataframe from dictionary
res = pd.DataFrame.from_dict(c_res, orient='index')
return res
def original(df):
res = pd.DataFrame(index=range(2000, 2026), columns=['AvgFuelConsumption'])
for y in range(2000, 2026):
res.loc[y, 'AvgFuelConsumption'] = df[(df["StartYear"]<=int(y))&(df["EndYear"]>=int(y))]["AvgFuelConsumption"].mean(axis=0)
return res
%timeit jp(df) # 4.17ms
%timeit original(df) # 54.8ms