Pandas入门实践2 -数据处理

为了准备数据进行分析,我们需要执行数据处理。在本节中,我们将学习如何清理和重新格式化数据(例如,重命名列和修复数据类型不匹配)、对其进行重构/整形,以及对其进行丰富(例如,离散化列、计算聚合和组合数据源)。

数据清洗

在本节中,我们将介绍如何创建、重命名和删除列;类型转换;和排序-所有这些都使我们的分析更容易。我们将使用NYC Open Data提供的2019年黄色出租车出行数据。

import pandas as pd

taxis = pd.read_csv('../data/2019_Yellow_Taxi_Trip_Data.csv')
taxis.head()

Pandas入门实践2 -数据处理_第1张图片

数据源:NYC Open Data

删除列
让我们从删除ID列和store_and_fwd_flag列开始,我们不会使用这些列。

mask = taxis.columns.str.contains('id$|store_and_fwd_flag', regex=True)
columns_to_drop = taxis.columns[mask]
columns_to_drop

'''
Index(['vendorid', 'ratecodeid', 'store_and_fwd_flag', 'pulocationid',
       'dolocationid'],
      dtype='object')
'''
taxis = taxis.drop(columns=columns_to_drop)
taxis.head()

Pandas入门实践2 -数据处理_第2张图片
提示:另一种方法是选择要保留的列:taxis.loc[:,~mask]

重命名列

taxis = taxis.rename(
    columns={
        'tpep_pickup_datetime': 'pickup', 
        'tpep_dropoff_datetime': 'dropoff'
    }
)
taxis.columns

'''
Index(['pickup', 'dropoff', 'passenger_count', 'trip_distance', 'payment_type',
       'fare_amount', 'extra', 'mta_tax', 'tip_amount', 'tolls_amount',
       'improvement_surcharge', 'total_amount', 'congestion_surcharge'],
      dtype='object')
'''

类型转换
注意到数据类型有什么问题吗?

taxis.dtypes

'''
pickup                    object
dropoff                   object
passenger_count            int64
trip_distance            float64
payment_type               int64
fare_amount              float64
extra                    float64
mta_tax                  float64
tip_amount               float64
tolls_amount             float64
improvement_surcharge    float64
total_amount             float64
congestion_surcharge     float64
dtype: object
'''

pickup 和 dropoff 都应存储为日期时间。让我们解决这个问题:

taxis[['pickup', 'dropoff']] = \
    taxis[['pickup', 'dropoff']].apply(pd.to_datetime)
taxis.dtypes

'''
pickup                   datetime64[ns]
dropoff                  datetime64[ns]
passenger_count                   int64
trip_distance                   float64
payment_type                      int64
fare_amount                     float64
extra                           float64
mta_tax                         float64
tip_amount                      float64
tolls_amount                    float64
improvement_surcharge           float64
total_amount                    float64
congestion_surcharge            float64
dtype: object
'''

提示:还有其他方法可以执行类型转换。对于数值,我们可以使用pd.to_numeric()函数,稍后我们将看到astype()方法,这是一个更通用的方法。

创建新列
让我们为每一行计算以下内容:

  1. 行程时间
  2. 小费百分比
  3. 税金、通行费、规费及附加总额
  4. 出租车的平均速度
taxis = taxis.assign(
    elapsed_time=lambda x: x.dropoff - x.pickup, # 1
    cost_before_tip=lambda x: x.total_amount - x.tip_amount,
    tip_pct=lambda x: x.tip_amount / x.cost_before_tip, # 2
    fees=lambda x: x.cost_before_tip - x.fare_amount, # 3
    avg_speed=lambda x: x.trip_distance.div(
        x.elapsed_time.dt.total_seconds() / 60 / 60
    ) # 4
)

我们的新列将添加到右侧:

taxis.head(2)

Pandas入门实践2 -数据处理_第3张图片
注意事项:
我们使用lambda函数是为了:1)避免重复输入taxis; 2)能够以创建cost_before_tip和elapsed_time列的相同方法访问它们。
要创建一个新列,我们也可以使用df['new_col'] =

按值排序
我们可以使用sort_values()方法根据任意数量的列进行排序:

taxis.sort_values(['passenger_count', 'pickup'], ascending=[False, True]).head()

Pandas入门实践2 -数据处理_第4张图片
要挑选最大/最小的行,请使用nlargest()/nsmallest()。查看3个运行时间最长的行程:

taxis.nlargest(3, 'elapsed_time')

Pandas入门实践2 -数据处理_第5张图片

使用索引

到目前为止,我们还没有真正使用索引,因为它只是一个行号;然而,我们可以改变索引中的值来访问pandas的其他功能。

设置和排序索引
目前,我们有一个RangeIndex,但我们可以通过在调用set_index()时指定一个datetime列来切换到DatetimeIndex:

taxis = taxis.set_index('pickup')
taxis.head(3)

Pandas入门实践2 -数据处理_第6张图片
既然我们有一个完整数据集的样本,让我们按取件时间对索引进行排序:

taxis = taxis.sort_index()

提示:taxis.sort_index(axis=1)将按名称对列进行排序。轴参数在整个pandas中都存在:axis=0目标行,axis=1目标列。

现在我们可以根据日期时间从数据中选择范围,就像我们选择行号一样:

taxis['2019-10-23 07:45':'2019-10-23 08']

Pandas入门实践2 -数据处理_第7张图片
当不指定范围时,我们使用loc[]:

taxis.loc['2019-10-23 08']

Pandas入门实践2 -数据处理_第8张图片
重置索引
我们将在本节后面处理时间序列,但有时我们希望将索引重置为行号并恢复列。我们可以使用reset_index()方法:

taxis = taxis.reset_index()
taxis.head()

Pandas入门实践2 -数据处理_第9张图片

重塑数据

我们正在使用的出租车数据集是一种有利于分析的格式。但情况并非总是如此。现在我们来看一下TSA的旅客吞吐量数据,它将2021年的吞吐量与2020年和2019年的同一天进行了比较:

tsa = pd.read_csv('../data/tsa_passenger_throughput.csv', parse_dates=['Date'])
tsa.head()

Pandas入门实践2 -数据处理_第10张图片
数据源:TSA.gov

首先,我们将列名称小写,并将第一个单词(例如,2021年旅客吞吐量),以使其更易于处理:

tsa = tsa.rename(columns=lambda x: x.lower().split()[0])
tsa.head()

Pandas入门实践2 -数据处理_第11张图片
现在,我们可以重塑它。

Melting(熔化)
Melting(熔化)有助于将数据转换为长格式。现在,我们在一列中显示了所有的旅客吞吐量:

tsa_melted = tsa.melt(
    id_vars='date', # 唯一标识行的列(可以是多个)
    var_name='year', # 通过熔化创建的新列的名称
    value_name='travelers' # 包含来自已经融化列的值的新列的名称
)
tsa_melted.sample(5, random_state=1) # show some random entries

Pandas入门实践2 -数据处理_第12张图片
为了将其转换为旅客吞吐量的时间序列,我们需要将date列中的年份替换为year列中的年份。否则,我们就把往年的数字标错了年份。

tsa_melted = tsa_melted.assign(
    date=lambda x: pd.to_datetime(x.year + x.date.dt.strftime('-%m-%d'))
)
tsa_melted.sample(5, random_state=1)

Pandas入门实践2 -数据处理_第13张图片
这就留下了一些空值(数据集中不存在的日期):

tsa_melted.sort_values('date').tail(3)

Pandas入门实践2 -数据处理_第14张图片
可以使用dropna()方法删除这些内容:

tsa_melted = tsa_melted.dropna()
tsa_melted.sort_values('date').tail(3)

Pandas入门实践2 -数据处理_第15张图片
旋转
使用融合的数据,我们可以透视数据,以比较不同年份特定日期的TSA旅客吞吐量:

tsa_pivoted = tsa_melted\
    .query('date.dt.month == 3 and date.dt.day <= 10')\
    .assign(day_in_march=lambda x: x.date.dt.day)\
    .pivot(index='year', columns='day_in_march', values='travelers')
tsa_pivoted

在这里插入图片描述
重要提示:我们没有介绍unstack()和stack()方法,它们分别是透视和融化的附加方法。
当我们有一个多层次的索引时,这些就派上用场了(例如,如果我们对多列运行set_index())

转置

tsa_pivoted.T

Pandas入门实践2 -数据处理_第16张图片
合并

我们通常会在假期前后观察航空旅行的变化,因此在TSA数据集中添加有关日期的信息可以提供更多上下文。holidays.csv文件包含美国的几个主要节日:

holidays = pd.read_csv('../data/holidays.csv', parse_dates=True, index_col='date')
holidays.loc['2019']

Pandas入门实践2 -数据处理_第17张图片
将假日与TSA旅客吞吐量数据合并将为我们的分析提供更多背景:

tsa_melted_holidays = tsa_melted\
    .merge(holidays, left_on='date', right_index=True, how='left')\
    .sort_values('date')
tsa_melted_holidays.head()

Pandas入门实践2 -数据处理_第18张图片
提示:这个方法有很多参数,所以一定要查阅文档。要追加行,请查看pd.concat()函数。

我们可以更进一步,把每个假期的前后几天标记为假期的一部分。这将更容易比较跨年的假日旅行,并寻找假日前后旅行的任何上升:

tsa_melted_holiday_travel = tsa_melted_holidays.assign(
    holiday=lambda x:
        x.holiday\
            .fillna(method='ffill', limit=1)\
            .fillna(method='bfill', limit=2)
)

提示:查看文档以获得fillna()方法可用功能的完整列表。

请注意,我们现在有了每个假日后一天和前两天的值。2019年的感恩节是11月28日,所以26日、27日、29日都排满了。因为我们只替换空值,所以我们不会用平安夜的前向填充覆盖Christmas Day:

tsa_melted_holiday_travel.query(
    'year == "2019" and '
    '(holiday == "Thanksgiving" or holiday.str.contains("Christmas"))'
)

Pandas入门实践2 -数据处理_第19张图片

聚合和分组

在重塑和清理数据之后,我们可以执行聚合,以各种方式汇总数据。在本节中,我们将探索如何使用透视表、交叉表和分组依据操作来聚合数据。

数据透视表

我们可以构建一个透视表来比较数据集中各年的假日旅行情况:

tsa_melted_holiday_travel.pivot_table(
    index='year', columns='holiday', 
    values='travelers', aggfunc='sum'
)

在这里插入图片描述
我们可以在这个结果上使用pct_change()方法来查看哪些假日旅行时段的旅行变化最大:

tsa_melted_holiday_travel.pivot_table(
    index='year', columns='holiday', 
    values='travelers', aggfunc='sum'
).pct_change()

Pandas入门实践2 -数据处理_第20张图片
让我们创建最后一个包含列和行小计的透视表,沿着对格式进行一些改进。首先,我们为所有浮动设置一个显示选项:

pd.set_option('display.float_format', '{:,.0f}'.format)

接下来,我们将圣诞前夜和圣诞节分组在一起,同样,将新年前夜和元旦分组在一起,并创建透视表:

import numpy as np

tsa_melted_holiday_travel.assign(
    holiday=lambda x: np.where(
        x.holiday.str.contains('Christmas|New Year', regex=True), 
        x.holiday.str.replace('Day|Eve', '', regex=True).str.strip(), 
        x.holiday
    )
).pivot_table(
    index='year', columns='holiday', 
    values='travelers', aggfunc='sum', 
    margins=True, margins_name='Total'
)

Pandas入门实践2 -数据处理_第21张图片
在继续之前,让我们重置显示选项:

pd.reset_option('display.float_format')

时间序列

在处理时间序列数据时,pandas为我们提供了额外的功能,不仅可以比较数据集中的观测结果,还可以利用它们在时间上的关系来分析数据。在本节中,我们将看到一些这样的操作,用于选择日期/时间范围、计算随时间的变化、执行窗口计算以及将数据重新采样到不同的日期/时间间隔。

根据日期和时间选择

taxis = taxis.set_index('dropoff').sort_index()

我们前面看到可以对日期时间进行切片:

taxis['2019-10-24 12':'2019-10-24 13']

Pandas入门实践2 -数据处理_第22张图片
我们也可以用简写来表示这个范围。注意我们必须在这里使用loc[]:

taxis.loc['2019-10-24 12']

Pandas入门实践2 -数据处理_第23张图片
我们可以使用between_time()方法提取任意一天某个特定时间范围内发生的下降:

taxis.between_time('12:00', '13:00')

Pandas入门实践2 -数据处理_第24张图片
提示:at_time()方法可用于提取给定时间的所有条目(e.g., 12:35:27)。

最后,head()和tail()将我们限制在一定数量的行,但是我们可能对数据的前/后2小时(或任何其他时间间隔)内的行感兴趣,在这种情况下,我们应该使用first()/ last():

taxis.first('2H')

Pandas入门实践2 -数据处理_第25张图片
在本节的其余部分,我们将使用TSA旅客吞吐量数据。让我们从设置date列的索引开始:

tsa_melted_holiday_travel = tsa_melted_holiday_travel.set_index('date')

计算随时间的变化

tsa_melted_holiday_travel.loc['2020'].assign(
    one_day_change=lambda x: x.travelers.diff(),
    seven_day_change=lambda x: x.travelers.diff(7),
).head(10)

Pandas入门实践2 -数据处理_第26张图片
提示:要执行减法以外的运算,请查看shift()方法。它还使跨列执行操作成为可能。

重采样

我们可以使用重采样将时间序列数据聚合到一个新的频率:

tsa_melted_holiday_travel['2019':'2021-Q1'].select_dtypes(include='number')\
    .resample('Q').agg(['sum', 'mean', 'std'])

Pandas入门实践2 -数据处理_第27张图片
窗口计算

窗口计算类似于分组依据计算,不同之处在于执行计算的组不是静态的-它可以移动或扩展。Pandas提供用于构造各种窗口的功能,包括移动/滚动窗口,展开窗口(例如,在时间序列中直到当前日期的累积和或平均值),以及指数加权移动窗口(对更近的观测值比更远的观测值更加权)。这里我们只看滚动和展开计算。

执行窗口计算与按计算分组非常相似-我们首先定义窗口,然后指定聚合:

tsa_melted_holiday_travel.loc['2020'].assign(
    **{
        '7D MA': lambda x: x.rolling('7D').travelers.mean(),
        'YTD mean': lambda x: x.expanding().travelers.mean()
      }
).head(10)

Pandas入门实践2 -数据处理_第28张图片
要想了解到底发生了什么,最好把原始数据和结果可视化,下面我们就来先睹为快,用pandas绘图。首先,在notebook中嵌入SVG格式图的一些设置:

import matplotlib_inline
from utils import mpl_svg_config

matplotlib_inline.backend_inline.set_matplotlib_formats(
    'svg', # output images using SVG format
    **mpl_svg_config('section-2') # optional: configure metadata
)

现在,我们调用plot()方法来可视化数据:

_ = tsa_melted_holiday_travel.loc['2020'].assign(
    **{
        '7D MA': lambda x: x.rolling('7D').travelers.mean(),
        'YTD mean': lambda x: x.expanding().travelers.mean()
      }
).plot(title='2020 TSA Traveler Throughput', ylabel='travelers', alpha=0.8)

Pandas入门实践2 -数据处理_第29张图片

你可能感兴趣的:(pandas,python,数据分析)