基于Python的数据科学(7):Pandas基础

介绍

        Pandas 是一个强大的Python库,专为数据操作和分析而设计。Pandas 提供了高效的数据结构和数据分析工具,特别适合处理带有标签的数据。本文将详细介绍Pandas的基本功能和用法。

安装与导入

        首先,确保你已经安装了Pandas库,如果没有安装,可以使用pip命令进行安装:

pip install pandas

        然后,在你的Python脚本中导入Pandas:

import pandas as pd
pd.__version__

Pandas数据结构

        Pandas主要有两种数据结构:Series和DataFrame。Series是一维数组,DataFrame是二维数组。接下来我们将详细介绍这两种数据结构。

Series

创建Series

        Series可以通过列表或数组创建:

data_pd = pd.Series([0.25, 0.5, 0.75, 1.0])
print("Pandas series from list: \n", data_pd)

numpy_arr = np.arange(5)
data_pd = pd.Series(numpy_arr)
print("Pandas series from numpy array: \n", data_pd)

Series属性

        Series对象有许多有用的属性:

print("Data values: ", data_pd.values)
print("Data index:  ", data_pd.index)

Series索引

print("Data[1]: ", data_pd[1])
print("Data[-2:]: \n", data_pd[-2:])

data_pd = pd.Series([0.25, 0.5, 0.75, 1.0], index=['a', 'b', 'c', 'd'])
print("Data['b']: ", data_pd['b'])

index = ['a', 'b', 'c', 'd', 3]
data_pd = pd.Series(numpy_arr, index=index)
print("Index['a']: ", data_pd['a'])
print("Index[3]: ", data_pd[3])

Pandas和字典

some_population_dict = {'Chiangrai': 11111, 'Pathum Thani': 22222, 'Bangkok': 33333, 'Chiangmai': 44444}
data_pd = pd.Series(some_population_dict)
print("Population['Bangkok']: ", data_pd['Bangkok'])
print("Population['Pathumthan':'Chiangmai']: \n", data_pd['Pathum Thani': 'Chiangmai'])

操作Series

data_pd['e'] = 99
print("Data with e: \n", data_pd)
print("a in data?: ", 'a' in data_pd)
print("All keys: ", data_pd.keys())
print("All items: ", list(data_pd.items()))
print("All values: ", data_pd.values)
print("Data[(data > 0.3) & (data < 0.8)]: \n", data_pd[(data_pd > 0.3) & (data_pd < 0.8)])
print("Data[['a', 'e']]: \n", data_pd[['a', 'e']])

iloc 和 loc

data = pd.Series(['a', 'b', 'c'], index=(1, 3, 5))
print("Data[1]: ", data[1])
print("Data[1:3]: \n", data[1:3])

print("Data loc [1]: ", data.loc[1])
print("Data loc [1:3]: \n", data.loc[1:3])
print("Data iloc [1]: ", data.iloc[1])
print("Data iloc [1:3]: \n", data.iloc[1:3])

DataFrame

创建DataFrame

        DataFrame是一种二维数组,可以通过多个方式创建,例如从字典、列表或Numpy数组。

some_population_dict = {'Chiangrai': 11111, 'Pathum Thani': 22222, 'Bangkok': 33333, 'Chiangmai': 44444}
some_area_dict = {'Chiangrai': 999, 'Pathum Thani': 888, 'Bangkok': 777, 'Chiangmai': 666, 'Syria': 333}
states = pd.DataFrame({'population': some_population_dict, 'area': some_area_dict})
print("Everything: ")
print(states)

DataFrame索引

print("Only Chiangrai to Bangkok: ")
print(states['Chiangrai': 'Bangkok'])

print("Only population column: ")
print(states['Chiangrai': 'Bangkok']['population'])
print(states['population']['Chiangrai': 'Bangkok'])
print("Only area column with everything")
print(states['area'])
print(states[:]['area'])

DataFrame属性

print("Index: ", states.index)
print("Index[-1]: ", states.index[-1])
print("Columns: ", states.columns)
print("Columns[0:1]: ", states.columns[0:1])

其他创建方式

population_series = pd.Series(some_population_dict)
pd_from_series = pd.DataFrame(population_series, columns=['population'])
print("PD from series: \n", pd_from_series)

data = [{'a': i, 'b': 2 * i} for i in range(3)]
pd_from_list_dict = pd.DataFrame(data, index=[1, 2, 3])
print("PD from list of dict: \n", pd_from_list_dict)

data_numpy = np.random.rand(3, 2)
index = ['a', 'b', 'c']
columns = ['foo', 'bar']
pd_from_numpy = pd.DataFrame(data_numpy, index=index, columns=columns)
print("PD from numpy: \n", pd_from_numpy)

操作DataFrame

data['density'] = data['pop'] / data['area']
print(data)

print("==First row using iloc==")
print(data.iloc[0])

print("==First three rows, first two columns using iloc==")
print(data.iloc[:3, :2])

print("==Use loc for explicit index==")
print(data.loc[:'Bangkok', :'pop'])

print("==chain iloc and loc==")
print(data.loc[:'Bangkok'].iloc[:, :2])

print("==masking + fancy==")
print(data.loc[data.density > 20, ['pop', 'density']])

数据处理

处理缺失数据

import numpy as np
print("==Fill na with 0==")
print(df.fillna(0))

print("==Fill all na with mean==")
print(df.fillna(df.mean()))

print("replace df.mean() for col 1 with in place")
df[1].fillna(df[1].mean(), inplace=True)
print(df)

print("==Interpolate==")
print(df.interpolate(method='values'))

连接数据

data_numpy = np.random.rand(3, 2)
index = ['Bangkok', 'Chiangmai', 'Samut Prakan']
columns = ['Population', 'Area']
pd_from_numpy = pd.DataFrame(data_numpy, index=index, columns=columns)
print("==First dataframe==")
print(pd_from_numpy)

data_numpy2 = np.random.rand(4, 3)
index2 = ['Bangkok', 'Chiangmai', 'Samut Prakan', 'Pathum Thani']
columns2 = ['HDI', 'Temperature', 'GDP']
pd_from_numpy2 = pd.DataFrame(data_numpy2, index=index2, columns=columns2)
print("==Second dataframe==")
print(pd_from_numpy2)

print("==Normal concat along axis 1==")
print(pd.concat([pd_from_numpy, pd_from_numpy2], axis=1))

print("==Concat with join inner==")
print(pd.concat([pd_from_numpy, pd_from_numpy2], axis=1, join='inner'))

合并数据

left = pd.DataFrame({'ID': ['001', '002', '003', '005'], 'DS': ['B', 'B', 'B', 'C+'], 'SAD': ['A', 'B', 'C+', 'F']})
right = pd.DataFrame({'ID': ['001', '002', '003', '004'], 'HCI': ['B+', 'A', 'A', 'B+'], 'SDQI': ['A', 'A', 'B+', 'B']})
result = pd.merge(left, right, on='ID')
print(result)

result = pd.merge(left, right, on='ID', how="outer")
print(result)

result = pd.merge(left, right, on='ID', how="left")
print(result)

聚合数据

df = pd.DataFrame([('bird', 'Falconiformes', 389.0), ('bird', 'Psittaciformes', 24.0), ('mammal', 'Carnivora', 80.2), ('mammal', 'Primates', np.nan), ('mammal', 'Carnivora', 58)], index=['falcon', 'parrot', 'lion', 'monkey', 'leopard'], columns=('class', 'order', 'max_speed'))
grouped = df.groupby('class')
print(grouped.sum())

grouped = df.groupby('order')
print(grouped.sum())

grouped = df.groupby(['class', 'order'])
print(grouped.sum())

print(df.groupby(['class'])['max_speed'].median())

import seaborn as sns
planets = sns.load_dataset('planets')
print(planets.groupby('method').sum())
print(planets.groupby('method')['orbital_period'].median())

时间序列

        Pandas提供了强大的时间序列处理功能,可以方便地进行时间序列的操作和分析。

创建时间序列

date = pd.to_datetime("2015-07-04")
print(type(date))
date.strftime('%A')

date + pd.to_timedelta(np.arange(12), 'D')

index = pd.to_datetime(['2014-07-04', '2014-08-04', '2015-07-04', '2015-08-04'])
data = pd.DataFrame(np.random.rand(4,2), index=index, columns=['Apple', 'Orange'])
print(data)

print(data['2014-07-04':'2015-07-04'])
print(data['2015'])

时间序列操作

pd.date_range('2015-07-03', '2015-07-10')
pd.date_range('2015-07-03', periods = 8)
pd.date_range('2015-07-03', periods = 8, freq='H')
pd.date_range('2015-07-03', periods = 18, freq='MS')
pd.date_range('2015-07-03', periods = 18, freq='2H30T')

股票数据示例

from pandas_datareader import data
import matplotlib.pyplot as plt

goog = data.DataReader('GOOG', start='2004', end='2016', data_source='yahoo')
goog_close = goog['Close']
goog_close.plot()

goog_close.plot(alpha=0.2, style='-')
goog_close.resample('BA').mean().plot(style='o')
goog_close.asfreq('BA').plot(style='--')

滚动窗口

rolling = goog_close.rolling(365, center = True)
data = pd.DataFrame({'input': goog_close, 'one_year rolling mean': rolling.mean(), 'one_year rolling std': rolling.std()})
data.plot(style=['-', '--', ':'])

自行车统计数据示例

data = pd.read_csv('data/FremontBridge.csv', index_col='Date', parse_dates=True)
data.columns = ['West', 'East']
data['Total'] = data['East'] + data['West']
data.fillna(0, inplace=True)

weekly = data.resample('W').agg('sum')
weekly.plot(style=[":", "--", '-'])
plt.ylabel('Weekly bicycle count')

by_time = data.groupby(data.index.time).mean()
by_time.plot()

by_day = data.groupby(data.index.dayofweek).mean()
by_day.index = ['Mon', 'Tues', 'Wed', 'Thurs', 'Fri', 'Sat', 'Sun']
by_day.plot(style=[':', '--', '-'])

任务 1

  1. 将数据文件夹中的 "howlongwelive.csv" 文件加载到 DataFrame 中 提示:Reproducibility Tips: Absolute vs. relative paths | Kaggle

  2. 打印前两行和最后两行数据

  3. 打印 DataFrame 的形状

  4. 打印特征(列)名称

  5. 使用 .describe() 打印数据摘要

  6. 由于 Hepatatis B 列有很多空值,并且与 Diptheria 高度相关,删除 Hepatatis 列。另外,由于 Population 列有太多空值,也将其删除

  7. 将 Status 转换为 0 或 1(0 表示 Developing,1 表示 Developed)

  8. 将列名 thinness 1-19 years 重命名为 thinness 10-19 years

  9. 获取除 Life Expectancy 外的所有列,并转换为名为 X 的 numpy 数组

  10. 获取 Life Expectancy 列并转换为名为 y 的 numpy 数组

import pandas as pd

# 1. 将数据文件夹中的 "howlongwelive.csv" 文件加载到 DataFrame 中
df = pd.read_csv('data/howlongwelive.csv')

# 2. 打印前两行和最后两行数据
print("前两行数据:")
print(df.head(2))
print("最后两行数据:")
print(df.tail(2))

# 3. 打印 DataFrame 的形状
print("DataFrame 的形状:", df.shape)

# 4. 打印特征(列)名称
print("特征(列)名称:", df.columns)

# 5. 使用 .describe() 打印数据摘要
print("数据摘要:")
print(df.describe())

# 6. 由于 Hepatatis B 列有很多空值,并且与 Diptheria 高度相关,删除 Hepatatis 列。另外,由于 Population 列有太多空值,也将其删除
df.drop(['Hepatatis B', 'Population'], axis=1, inplace=True)

# 7. 将 Status 转换为 0 或 1(0 表示 Developing,1 表示 Developed)
df['Status'] = df['Status'].apply(lambda x: 0 if x == 'Developing' else 1)

# 8. 将列名 thinness 1-19 years 重命名为 thinness 10-19 years
df.rename(columns={'thinness 1-19 years': 'thinness 10-19 years'}, inplace=True)

# 9. 获取除 Life Expectancy 外的所有列,并转换为名为 X 的 numpy 数组
X = df.drop('Life expectancy ', axis=1).values

# 10. 获取 Life Expectancy 列并转换为名为 y 的 numpy 数组
y = df['Life expectancy '].values

处理缺失数据

np.nan

import numpy as np

print("np.nan 的类型:", type(np.nan))
print("np.nan 减 1 的结果:", np.nan - 1)  # 任何与 nan 进行的运算结果仍然是 nan

vals2 = np.array([1, np.nan, 3, 4])
print("Sum:", vals2.sum())
print("Nansum:", np.nansum(vals2))
print("Nanmin:", np.nanmin(vals2))
print("Nanmax:", np.nanmax(vals2))

创建包含 np.nan 和 None 的 pd.Series

import pandas as pd

dfs = pd.Series([1, None, np.nan])
print("Pandas 自动将 None 转换为 nan")
print(dfs)

dfs = pd.Series(["Hello", None, np.nan])
print("Pandas 不会转换,因为字符串是对象")
print(dfs)

dfs = pd.Series([True, None, np.nan])
print("Pandas 不会转换,布尔值被强制转换为对象")
print(dfs)

检查空值

dfs = pd.Series([1, np.nan, "hello", None])
print("是否为空值:", dfs.isnull())

仅显示非空数据

print("仅显示非空数据")
print(dfs[dfs.notnull()])

删除空值

print("删除所有空值")
print(dfs.dropna())  # 这不是就地操作
print(dfs)  # 这将返回旧的副本

# 删除包含空值的列,使用 axis = 1
df = pd.DataFrame([[1, np.nan, 2],
                   [2, 3, 5],
                   [np.nan, 4, np.nan],
                   [4, np.nan, np.nan]])

print(df.dropna(axis=1))  # 默认 axis = 0

# 如果所有值都是 nan,则删除列
df[3] = np.nan  # 创建新列
print(df.dropna(axis=1, how="all"))

用值填充空值

# 用 0 填充所有空值
print("用 0 填充所有空值")
print(df.fillna(0))  # 这不是就地操作

# 用平均值填充所有空值
print("用平均值填充所有空值")
print(df.fillna(df.mean()))

# 用平均值替换第 1 列的缺失值并就地操作
df[1].fillna(df[1].mean(), inplace=True)
print(df)

# 用插值法填充缺失值
print("用插值法填充缺失值")
print(df.interpolate(method='values'))

合并数据集

data_numpy = np.random.rand(3, 2)  # 形状为 3x2
index = ['Bangkok', 'Chiangmai', 'Samut Prakan']
columns = ['Population', 'Area']
pd_from_numpy = pd.DataFrame(data_numpy, index=index, columns=columns)
print("第一个 DataFrame:")
print(pd_from_numpy)

data_numpy2 = np.random.rand(4, 3)
index2 = ['Bangkok', 'Chiangmai', 'Samut Prakan', 'Pathum Thani']
columns2 = ['HDI', 'Temperature', 'GDP']
pd_from_numpy2 = pd.DataFrame(data_numpy2, index=index2, columns=columns2)
print("第二个 DataFrame:")
print(pd_from_numpy2)

# 正常连接(沿轴 1)
print("沿轴 1 正常连接:")
print(pd.concat([pd_from_numpy, pd_from_numpy2], axis=1))  # 默认 join='outer',即完全外连接

# 内连接
print("内连接:")
print(pd.concat([pd_from_numpy, pd_from_numpy2], axis=1, join='inner'))

基于ID合并数据集

left = pd.DataFrame({'ID': ['001', '002', '003', '005'],
                      'DS': ['B', 'B', 'B', 'C+'],
                      'SAD': ['A', 'B', 'C+', 'F']})
print("左表:")
print(left)

right = pd.DataFrame({'ID': ['001', '002', '003', '004'],
                      'HCI': ['B+', 'A', 'A', 'B+'],
                      'SDQI': ['A', 'A', 'B+', 'B']})
print("右表:")
print(right)

# 内连接
result = pd.merge(left, right, on='ID')  # 默认 how='inner'
print("内连接结果:")
print(result)

# 外连接
result = pd.merge(left, right, on='ID', how="outer")
print("外连接结果:")
print(result)

# 左连接
result = pd.merge(left, right, on='ID', how="left")
print("左连接结果:")
print(result)

聚合

df = pd.DataFrame([('bird', 'Falconiformes', 389.0),
                    ('bird', 'Psittaciformes', 24.0),
                    ('mammal', 'Carnivora', 80.2),
                    ('mammal', 'Primates', np.nan),
                    ('mammal', 'Carnivora', 58)],
                   index=['falcon', 'parrot', 'lion', 'monkey', 'leopard'],
                   columns=('class', 'order', 'max_speed'))
print("DataFrame:")
print(df)

grouped = df.groupby('class')  # 返回一个 DataFrameGroupBy 对象
print("按 class 分组求和:")
print(grouped.sum())

grouped = df.groupby('order')
print("按 order 分组求和:")
print(grouped.sum())

grouped = df.groupby(['class', 'order'])
print("按 class 和 order 分组求和:")
print(grouped.sum())

print("按 class 分组求中位数:")
print(df.groupby(['class'])['max_speed'].median())

任务 2

  1. 继续使用 "howlongwelive.csv" 文件,检查每列有多少缺失数据
  2. 使用均值填充所有缺失数据
  3. 按国家进行分组。哪个国家的平均预期寿命最低/最高?
  4. 按状态进行分组。发达国家和发展中国家的预期寿命是否有显著差异?
  5. 手动创建另一个包含两列的数据框。第一列是与国家列相同的 ID 列。还添加另一列 Noise_level,并填充随机值(随意填写)。
  6. 根据 ID 列合并这两个数据集。
import pandas as pd
import numpy as np

# 1. 继续使用 "howlongwelive.csv" 文件,检查每列有多少缺失数据
df = pd.read_csv('data/howlongwelive.csv')
print("每列缺失数据的数量:")
print(df.isnull().sum())

# 2. 使用均值填充所有缺失数据
df.fillna(df.mean(), inplace=True)

# 3. 按国家分组,计算平均预期寿命
grouped_by_country = df.groupby('Country')['Life expectancy '].mean()
print("平均预期寿命最低的国家:", grouped_by_country.idxmin(), grouped_by_country.min())
print("平均预期寿命最高的国家:", grouped_by_country.idxmax(), grouped_by_country.max())

# 4. 按状态分组,计算平均预期寿命
grouped_by_status = df.groupby('Status')['Life expectancy '].mean()
print("按状态分组的平均预期寿命:")
print(grouped_by_status)

# 5. 创建另一个包含两列的数据框
noise_level = np.random.rand(len(df['Country'].unique()))
additional_df = pd.DataFrame({'Country': df['Country'].unique(), 'Noise_level': noise_level})

# 6. 根据 ID 列合并这两个数据集
merged_df = pd.merge(df, additional_df, on='Country')
print("合并后的 DataFrame:")
print(merged_df.head())

时间序列

        Pandas 是在金融建模的背景下开发的,因此它包含了一整套处理日期、时间和时间索引数据的工具。首先让我们了解 Python 如何处理日期和时间。

from datetime import datetime
datetime(year=2025, month=7, day=4)

        使用 dateutil 模块,可以从各种字符串格式解析日期。

from dateutil import parser
date = parser.parse("4th of July, 2015")
date

        一旦你有了一个 datetime 对象,就可以做一些事情,比如打印星期几:

date.strftime('%A, %D')

        我们可以类似地创建 numpy 类型的日期时间,它是一种非常有效的存储日期时间的方法。

date = np.array('2015-07-04', dtype=np.datetime64)  # ISO 日期
date

        由于其 numpy 类型,我们可以快速对其进行矢量化操作。

date + np.arange(12)

        Pandas 使用 Timestamp 对象,它结合了 datetime 和 dateutil 的易用性以及 numpy.datetime64 的高效存储和矢量化接口。从这些 Timestamp 对象组中,Pandas 可以构建 DatetimeIndex,可用于索引 Series 或 DataFrame 中的数据。

import pandas as pd
date = pd.to_datetime("2015-07-04")
print(type(date))
date.strftime('%A')

        使用 pd.to_timedelta 可以执行 numpy 风格的矢量化操作。

date + pd.to_timedelta(np.arange(12), 'D')  # 单位是纳秒,不支持 Y 和 M,因为每个月的纳秒数不相等

        当你开始通过时间戳索引数据时,Pandas 的时间序列工具真正变得有用。例如,我们可以构建一个具有时间索引数据的 Series 对象:

index = pd.to_datetime(['2014-07-04', '2014-08-04', '2015-07-04', '2015-08-04'])
data = pd.DataFrame(np.random.rand(4, 2), index=index, columns=['Apple', 'Orange'])
data

        使用切片访问行:

data['2014-07-04':'2015-07-04']

        有特殊的日期索引,例如传递年份:

data['2015']

        一个有用的方法是 date_range,它生成从指定开始和结束的日期:

pd.date_range('2015-07-03', '2015-07-10')

        我们可以指定周期而不是结束:

pd.date_range('2015-07-03', periods=8)

        如果我们想要 8 个周期,但以小时为单位,我们使用 freq 参数:

pd.date_range('2015-07-03', periods=8, freq='H')

股票

from pandas_datareader import data

goog = data.DataReader('GOOG', start='2004', end='2016', data_source='yahoo')
goog.head()

import matplotlib.pyplot as plt

goog_close = goog['Close']
goog_close.plot()

        时间序列数据的一个常见需求是以更高或更低的频率重新采样。这可以使用 resample() 方法,或更简单的 asfreq() 方法来完成。这两者之间的主要区别是 resample() 本质上是数据聚合,而 asfreq() 本质上是数据选择。

        让我们看看 Google 收盘价,当我们对数据进行降采样时,两者返回的结果有什么不同。在这里,我们将在营业年末对数据进行重新采样:

resample
goog_close.plot(alpha=0.2, style='-')
goog_close.resample('BA').mean().plot(style='o')  # BA = 营业年末
goog_close.asfreq('BA').plot(style='--')

        请注意区别:在每个点,resample 报告前一年的平均值,而 asfreq 报告年末的值。

        橙色点表示 goog_close.resample(),绿色虚线表示 goog_close.asfreq()。

        resample 和 asfreq 之间的区别:

ts = pd.Series(range(365), index=pd.date_range(start='20190101', end='20191231', freq='D'))
ts.head()
ts.asfreq(freq='Q')  # 季度

        这不是每个季度的平均值,而是每个季度最后一天的平均值:

        Pandas 有两个密切相关的方法:shift() 和 tshift()。简而言之,两者的区别在于 shift() 移动数据,而 tshift() 移动索引。在两种情况下,移动是以频率的倍数指定的。这里我们将同时移动 900 天:

fig, ax = plt.subplots(3, sharey=True)

goog_close = goog_close.asfreq('D', method='pad')

goog_close.plot(ax=ax[0])
goog_close.shift(900).plot(ax=ax[1])
goog_close.tshift(900).plot(ax=ax[2])

        为什么要移动?

        一个常见的上下文是计算一段时间内的差异。例如,我们使用移动后的值来计算 Google 股票在数据集期间的一年投资回报率:

ROI = 100 * (goog_close.tshift(-365) / goog_close)
ROI.plot()
plt.ylabel('Return on Investment')

        这有助于我们看到 Google 股票的整体趋势:迄今为止,投资 Google 最有利可图的时间是其 IPO 后不久和 2009 年中期的经济衰退。

滚动窗口

rolling = goog_close.rolling(365, center=True)
rolling

data = pd.DataFrame({'input': goog_close, 'one_year rolling mean': rolling.mean(), 'one_year rolling std': rolling.std()})
data.plot(style=['-', '--', ':'])

自行车计数

        示例:可视化西雅图自行车计数

data = pd.read_csv('data/FremontBridge.csv')
data = pd.read_csv('data/FremontBridge.csv', index_col='Date', parse_dates=True)
data.head()

data.columns = ['West', 'East']
data.head()

data.isna().sum()
data.fillna(0, inplace=True)

data['Total'] = data['East'] + data['West']
data.head()

任务 3

  1. 加载 "data/appl_1980_2014.csv" 到数据框 df 中
  2. 将 Date 列转换为 datetime 类型
  3. 然后仅筛选年份不小于 1987 的数据
  4. 按升序排序索引(最早的日期在前)- 使用 sort_index
  5. 我们想知道苹果在每个月底的表现,因此获取每列的平均值,并在每个月的最后一个营业日进行重新采样(即 BM)
  6. 顺便说一下,我们的数据中有多少个月?
  7. 比较今年和去年 High 列的差异,使用 shift(),其中差异为 this_year - last_year。绘制此图,x 轴为日期,y 轴为涨跌幅
  8. 对 Close 列执行窗口大小为 100 天的滚动平均线
  9. 使用以下代码加载微软数据:
import pandas as pd
import matplotlib.pyplot as plt
from pandas_datareader import data

# 1. 加载 "data/appl_1980_2014.csv" 文件
df = pd.read_csv('data/appl_1980_2014.csv')

# 2. 将 Date 列转换为 datetime 类型
df['Date'] = pd.to_datetime(df['Date'])

# 3. 过滤掉年份小于 1987 的数据
df = df[df['Date'].dt.year >= 1987]

# 4. 按升序排序索引(最早的日期在前)
df.sort_index(ascending=True, inplace=True)

# 5. 获取每列的平均值,在每个月的最后一个工作日进行重采样(即 BM)
monthly_avg = df.resample('BM', on='Date').mean()

# 6. 我们的数据中有多少个月?
num_months = len(monthly_avg)
print(f"数据中的月份数量: {num_months}")

# 7. 使用 shift() 比较今年与去年 High 列的差异,绘制图表
monthly_avg['High_diff'] = monthly_avg['High'].diff(12)
plt.figure(figsize=(10, 6))
plt.plot(monthly_avg.index, monthly_avg['High_diff'], label='High Difference')
plt.xlabel('Date')
plt.ylabel('Difference in High')
plt.title('Year-over-Year Difference in High Price')
plt.legend()
plt.show()

# 8. 对 Close 列执行窗口大小为 100 天的滚动平均
df['Close_MA100'] = df['Close'].rolling(window=100).mean()
plt.figure(figsize=(10, 6))
plt.plot(df['Date'], df['Close'], label='Close Price')
plt.plot(df['Date'], df['Close_MA100'], label='100-Day Moving Average')
plt.xlabel('Date')
plt.ylabel('Price')
plt.title('Apple Close Price and 100-Day Moving Average')
plt.legend()
plt.show()

# 9. 加载微软数据并比较回报率
microsoft = data.DataReader('MSFT', start='1987', end='2014', data_source='yahoo')
apple_2000_onward = df[df['Date'].dt.year >= 2000].copy()
microsoft_2000_onward = microsoft[microsoft.index.year >= 2000].copy()

apple_2000_onward['Return'] = apple_2000_onward['Close'] / apple_2000_onward['Close'].iloc[0]
microsoft_2000_onward['Return'] = microsoft_2000_onward['Close'] / microsoft_2000_onward['Close'].iloc[0]

plt.figure(figsize=(10, 6))
plt.plot(apple_2000_onward['Date'], apple_2000_onward['Return'], label='Apple Return')
plt.plot(microsoft_2000_onward.index, microsoft_2000_onward['Return'], label='Microsoft Return')
plt.xlabel('Date')
plt.ylabel('Return Rate')
plt.title('Apple vs Microsoft Return Rate (2000 Onward)')
plt.legend()
plt.show()

结语

        在本篇文章中,我们详细介绍了 Pandas 的基础知识,包括 Series 和 DataFrame 的创建、索引、切片、合并和聚合操作等。我们通过实际的代码示例演示了如何使用 Pandas 进行数据处理和分析,并完成了几个实际任务,以帮助大家更好地理解和掌握这些操作。

        通过这些内容,我们可以看到 Pandas 强大的数据处理能力,它不仅能够处理结构化数据,还能够与其他科学计算库(如 NumPy)无缝集成。Pandas 提供的这些功能,使得我们能够更高效地进行数据清洗、转换和分析,从而在数据科学项目中发挥重要作用。

        下一篇文章将介绍数据可视化工具——Matplotlib。我们将学习如何使用Matplotlib创建各种图表,以更直观地展示数据分析的结果。敬请期待!

如果你觉得这篇博文对你有帮助,请点赞、收藏、关注我,并且可以打赏支持我!

欢迎关注我的后续博文,我将分享更多关于人工智能、自然语言处理和计算机视觉的精彩内容。

谢谢大家的支持!

  • 23
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

会飞的Anthony

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值