数据分析-pandas2
数据分析-pandas2
接上述1
计算同环比
我们之前讲过一个统计月度销售额的例子,我们可以通过groupby
方法做分组聚合,也可以通过pivot_table
生成透视表,如下所示。
sale_df = pd.read_excel(r"./dates/2020年销售数据.xlsx")
sale_df['销售总额'] = sale_df['售价'] * sale_df['销售数量']
# print(sale_df.groupby('销售区域')['销售总额'].sum()['上海'])
# print(sale_df.groupby(['销售区域',sale_df['销售日期'].dt.month])['销售总额'].sum())
# print(pd.pivot_table(sale_df, index=["销售区域",sale_df['销售日期'].dt.month], values='销售总额',aggfunc='sum'))
sale_df['月份'] = sale_df['销售日期'].dt.month
sale_df['季度'] = sale_df['销售日期'].dt.quarter
sale_df['星期'] = sale_df['销售日期'].dt.weekday
result_df = sale_df.pivot_table(index='月份', values='销售总额', aggfunc='sum')
print(result_df)
在得到月度销售额之后,如果我们需要计算月环比,这里有两种方案。第一种方案是我们可以使用shift
方法对数据进行移动,将上一个月的数据与本月数据对齐,然后通过(本月销售额 - 上月销售额) / 上月销售额
来计算月环比,代码如下所示。
result_df['上月销售额'] = result_df.销售总额.shift(1)
result_df['环比'] = (result_df.销售总额 - result_df.上月销售额) / result_df.上月销售额
result_df.style.format(
formatter={'上月销售额': '{:.0f}', '环比': '{:.2%}'},
na_rep='hulue'
)
print(result_df)
更为简单的第二种方案是直接使用pct_change
方法计算变化的百分比,我们使用DataFrame
对象的pct_change
方法完成环比的计算。值得一提的是,pct_change
方法有一个名为periods
的参数,它的默认值是1
,计算相邻两项数据变化的百分比,这不就是我们想要的环比吗?如果我们有很多年的数据,在计算时把这个参数的值修改为12
,就可以得到相邻两年的月同比。
result_df['环比'] = result_df.pct_change()
print(result_df)
窗后计算
DataFrame
对象的rolling
方法允许我们将数据置于窗口中,然后用函数对窗口中的数据进行运算和处理。例如,我们获取了某只股票近期的数据,想制作5日均线和10日均线,那么就需要先设置窗口再进行运算。我们先用如下所示的代码读取2022年百度的股票数据,数据文件可以通过下面的链接来获取。 2022年股票数据.xlsx
gu_df = pd.read_excel(r"./dates/2022年股票数据.xlsx",sheet_name='BIDU', index_col="Date")
gu_df.sort_values(by='Date', inplace=True)
print(gu_df)
###
Date Open High Low Close Volume
0 2022-12-30 87.450 89.4100 87.4101 88.09 11926714
1 2022-12-29 87.625 89.5200 87.0600 89.13 12535405
上面的DataFrame
有Open
、High
、Low
、Close
、Volume
五个列,分别代表股票的开盘价、最高价、最低价、收盘价和成交量,接下来我们对百度的股票数据进行窗口计算。
gu_df.rolling(5).mean()
我们也可以在Series
上使用rolling
设置窗口并在窗口内完成运算,例如我们可以对上面的百度股票收盘价(Close
列)计算5日均线和10日均线,并使用merge
函数将其组装到一个DataFrame
对象中并绘制出双均线图,代码如下所示。
close_ma5 = gu_df.Close.rolling(5).mean()
print(close_ma5)
close_ma10 = gu_df.Close.rolling(10).mean()
result_df = pd.merge(close_ma5, close_ma10, left_index=True, right_index=True)
result_df.rename(columns={'Close_x': 'MA5', 'Close_y': 'MA10'}, inplace=True)
result_df.plot(kind='line', figsize=(10, 6))
plt.show()
相关性判定
在统计学中,我们通常使用协方差(covariance)来衡量两个随机变量的联合变化程度。如果变量 𝑋 的较大值主要与另一个变量 𝑌 的较大值相对应,而两者较小值也相对应,那么两个变量倾向于表现出相似的行为,协方差为正。如果一个变量的较大值主要对应于另一个变量的较小值,则两个变量倾向于表现出相反的行为,协方差为负。简单的说,协方差的正负号显示着两个变量的相关性。方差是协方差的一种特殊情况,即变量与自身的协方差。
𝑐𝑜𝑣(𝑋,𝑌)=𝐸((𝑋−𝜇)(𝑌−𝜐))=𝐸(𝑋⋅𝑌)−𝜇𝜐
如果 𝑋 和 𝑌 是统计独立的,那么二者的协方差为0,这是因为在 𝑋 和 𝑌 独立的情况下:
𝐸(𝑋⋅𝑌)=𝐸(𝑋)⋅𝐸(𝑌)=𝜇𝜐
协方差的数值大小取决于变量的大小,通常是不容易解释的,但是正态形式的协方差可以显示两变量线性关系的强弱。在统计学中,皮尔逊积矩相关系数就是正态形式的协方差,它用于度量两个变量 𝑋 和 𝑌 之间的相关程度(线性相关),其值介于-1
到1
之间。
𝑐𝑜𝑣(𝑋,𝑌)𝜎𝑋𝜎𝑌
估算样本的协方差和标准差,可以得到样本皮尔逊系数,通常用希腊字母 𝜌 表示。
𝜌=∑𝑖=1𝑛(𝑋𝑖−𝑋¯)(𝑌𝑖−𝑌¯)∑𝑖=1𝑛(𝑋𝑖−𝑋¯)2∑𝑖=1𝑛(𝑌𝑖−𝑌¯)2
我们用 𝜌 值判断指标的相关性时遵循以下两个步骤。
- 判断指标间是正相关、负相关,还是不相关。
- 当 $ \rho \gt 0 $,认为变量之间是正相关,也就是两者的趋势一致。
- 当 $ \rho \lt 0 $,认为变量之间是负相关,也就是两者的趋势相反。
- 当 $ \rho \approx 0 $,认为变量之间是不相关的,但并不代表两个指标是统计独立的。
- 判断指标间的相关程度。
- 当 $ \rho $ 的绝对值在 $ [0.6,1] $ 之间,认为变量之间是强相关的。
- 当 $ \rho $ 的绝对值在 $ [0.1,0.6) $ 之间,认为变量之间是弱相关的。
- 当 $ \rho $ 的绝对值在 $ [0,0.1) $ 之间,认为变量之间没有相关性。
皮尔逊相关系数适用于:
- 两个变量之间是线性关系,都是连续数据。
- 两个变量的总体是正态分布,或接近正态的单峰分布。
- 两个变量的观测值是成对的,每对观测值之间相互独立。
这里,我们顺便说一下,如果两组变量并不是来自于正态总体的连续值,我们该如何判断相关性呢?对于定序尺度(等级),我们可以使用斯皮尔曼秩相关系数,其计算公式如下所示: r s = 1 − 6 ∑ d i 2 n ( n 2 − 1 ) r_{s}=1-{\frac {6\sum d_{i}^{2}}{n(n^{2}-1)}} rs=1−n(n2−1)6∑di2 其中, d i = R ( X i ) − R ( Y i ) d_{i}=\operatorname {R} (X_{i})-\operatorname {R} (Y_{i}) di=R(Xi)−R(Yi),即每组观测中两个变量的等级差值, n n n为观测样本数。
对于定类尺度(类别),我们可以使用卡方检验的方式来判定其是否相关。其实很多时候,连续值也可以通过分箱的方式处理成离散的等级或类别,然后使用斯皮尔曼秩相关系数或卡方检验的方式来判定相关性。
DataFrame
对象的cov
方法和corr
方法分别用于计算协方差和相关系数,corr
方法有一个名为method
的参数,其默认值是pearson
,表示计算皮尔逊相关系数;除此之外,还可以指定kendall
或spearman
来计算肯德尔系数或斯皮尔曼秩相关系数。
索引扩展
我们再来看看Index
类型,它为Series
和DataFrame
对象提供了索引服务,有了索引我们就可以排序数据(sort_index
方法)、对齐数据(在运算和合并数据时非常重要)并实现对数据的快速检索(索引运算)。由于DataFrame
类型表示的是二维数据,所以它的行和列都有索引,分别是index
和columns
。Index
类型的创建的比较简单,通常给出data
、dtype
和name
三个参数即可,分别表示作为索引的数据、索引的数据类型和索引的名称。由于Index
本身也是一维的数据,索引它的方法和属性跟Series
非常类似,你可以尝试创建一个Index
对象,然后尝试一下之前学过的属性和方法在Index
类型上是否生效。接下来,我们主要看看Index
的几种子类型。
范围索引
范围索引是由具有单调性的整数构成的索引,我们可以通过RangeIndex
构造器来创建范围索引,也可以通过RangeIndex
类的类方法from_range
来创建范围索引,代码如下所示。
代码:
sales_data = np.random.randint(400, 1000, 12)
index = pd.RangeIndex(1, 13, name='月份')
ser = pd.Series(data=sales_data, index=index)
ser
输出:
月份
1 703
2 705
3 557
4 943
5 961
6 615
7 788
8 985
9 921
10 951
11 874
12 609
dtype: int64
分类索引
分类索引是由定类尺度构成的索引。如果我们需要通过索引将数据分组,然后再进行聚合操作,分类索引就可以派上用场。分类索引还有一个名为reorder_categories
的方法,可以给索引指定一个顺序,分组聚合的结果会按照这个指定的顺序进行呈现,代码如下所示。
代码:
sales_data = [6, 6, 7, 6, 8, 6]
index = pd.CategoricalIndex(
data=['苹果', '香蕉', '苹果', '苹果', '桃子', '香蕉'],
categories=['苹果', '香蕉', '桃子'],
ordered=True
)
ser = pd.Series(data=sales_data, index=index)
ser
输出:
苹果 6
香蕉 6
苹果 7
苹果 6
桃子 8
香蕉 6
dtype: int64
基于索引分组数据,然后使用sum
进行求和。
ser.groupby(level=0).sum()
输出:
苹果 19
香蕉 12
桃子 8
dtype: int64
指定索引的顺序。
ser.index = index.reorder_categories(['香蕉', '桃子', '苹果'])
ser.groupby(level=0).sum()
输出:
香蕉 12
桃子 8
苹果 19
dtype: int64
多级索引
Pandas 中的MultiIndex
类型用来表示层次或多级索引。可以使用MultiIndex
类的类方法from_arrays
、from_product
、from_tuples
等来创建多级索引,我们给大家举几个例子。
代码:
tuples = [(1, 'red'), (1, 'blue'), (2, 'red'), (2, 'blue')]
index = pd.MultiIndex.from_tuples(tuples, names=['no', 'color'])
index
输出:
MultiIndex([(1, 'red'),
(1, 'blue'),
(2, 'red'),
(2, 'blue')],
names=['no', 'color'])
代码:
arrays = [[1, 1, 2, 2], ['red', 'blue', 'red', 'blue']]
index = pd.MultiIndex.from_arrays(arrays, names=['no', 'color'])
index
输出:
MultiIndex([(1, 'red'),
(1, 'blue'),
(2, 'red'),
(2, 'blue')],
names=['no', 'color'])
代码:
sales_data = np.random.randint(1, 100, 4)
ser = pd.Series(data=sales_data, index=index)
ser
输出:
no color
1 red 43
blue 31
2 red 55
blue 75
dtype: int64
代码:
ser.groupby('no').sum()
输出:
no
1 74
2 130
dtype: int64
代码:
ser.groupby(level=1).sum()
输出:
color
blue 106
red 98
dtype: int64
代码:
stu_ids = np.arange(1001, 1006)
semisters = ['期中', '期末']
index = pd.MultiIndex.from_product((stu_ids, semisters), names=['学号', '学期'])
courses = ['语文', '数学', '英语']
scores = np.random.randint(60, 101, (10, 3))
df = pd.DataFrame(data=scores, columns=courses, index=index)
df
输出:
语文 数学 英语
学号学期
1001 期中937760
期末939884
1002 期中647871
期末707197
1003 期中728897
期末9910063
1004 期中807161
期末916272
1005 期中829567
期末847886
根据第一级索引分组数据,按照期中成绩占25%
,期末成绩占75%
的方式计算每个学生每门课的成绩。
代码:
df.groupby(level=0).agg(lambda x: x.values[0] * 0.25 + x.values[1] * 0.75)
输出:
语文 数学 英语
学号
100193.0092.7578.00
100268.5072.7590.50
100392.2597.0071.50
100488.2564.2569.25
100583.5082.2581.25
间隔索引
间隔索引顾名思义是使用固定的间隔范围充当索引,我们通常会使用interval_range
函数来创建间隔索引,代码如下所示。
代码:
index = pd.interval_range(start=0, end=5)
index
输出:
IntervalIndex([(0, 1], (1, 2], (2, 3], (3, 4], (4, 5]], dtype='interval[int64, right]')
IntervalIndex
有一个名为contains
的方法,可以检查范围内是否包含了某个元素,如下所示。
代码:
index.contains(1.5)
输出:
array([False, True, False, False, False])
IntervalIndex
还有一个名为overlaps
的方法,可以检查一个范围跟其他的范围是否有重叠,如下所示。
代码:
index.overlaps(pd.Interval(1.5, 3.5))
输出:
array([False, True, True, True, False])
如果希望间隔范围是左闭右开的状态,可以在创建间隔索引时通过closed='left'
来做到;如果希望两边都是关闭状态,可以将close
参数的值赋值为both
,代码如下所示。
代码:
index = pd.interval_range(start=0, end=5, closed='left')
index
输出:
IntervalIndex([[0, 1), [1, 2), [2, 3), [3, 4), [4, 5)], dtype='interval[int64, left]')
代码:
index = pd.interval_range(start=pd.Timestamp('2022-01-01'), end=pd.Timestamp('2022-01-04'), closed='both')
index
输出:
IntervalIndex([[2022-01-01, 2022-01-02], [2022-01-02, 2022-01-03], [2022-01-03, 2022-01-04]], dtype='interval[datetime64[ns], both]')
日期时间索引
DatetimeIndex
应该是众多索引中最复杂最重要的一种索引,我们通常会使用date_range()
函数来创建日期时间索引,该函数有几个非常重要的参数start
、end
、periods
、freq
、tz
,分别代表起始日期时间、结束日期时间、生成周期、采样频率和时区。我们先来看看如何创建DatetimeIndex
对象,再来讨论它的相关运算和操作,代码如下所示。
代码:
pd.date_range('2021-1-1', '2021-6-30', periods=10)
输出:
DatetimeIndex(['2021-01-01', '2021-01-21', '2021-02-10', '2021-03-02',
'2021-03-22', '2021-04-11', '2021-05-01', '2021-05-21',
'2021-06-10', '2021-06-30'],
dtype='datetime64[ns]', freq=None)
代码:
pd.date_range('2021-1-1', '2021-6-30', freq='W')
说明:
freq=W
表示采样周期为一周,它会默认星期日是一周的开始;如果你希望星期一表示一周的开始,你可以将其修改为freq=W-MON
;你也可以试着将该参数的值修改为12H
,M
,Q
等,看看会发生什么,相信你不难猜到它们的含义。
输出:
DatetimeIndex(['2021-01-03', '2021-01-10', '2021-01-17', '2021-01-24',
'2021-01-31', '2021-02-07', '2021-02-14', '2021-02-21',
'2021-02-28', '2021-03-07', '2021-03-14', '2021-03-21',
'2021-03-28', '2021-04-04', '2021-04-11', '2021-04-18',
'2021-04-25', '2021-05-02', '2021-05-09', '2021-05-16',
'2021-05-23', '2021-05-30', '2021-06-06', '2021-06-13',
'2021-06-20', '2021-06-27'],
dtype='datetime64[ns]', freq='W-SUN')
DatatimeIndex
可以跟DateOffset
类型进行运算,这一点很好理解,以为我们可以设置一个时间差让时间向前或向后偏移,具体的操作如下所示。
代码:
index = pd.date_range('2021-1-1', '2021-6-30', freq='W')
index - pd.DateOffset(days=2)
输出:
DatetimeIndex(['2021-01-01', '2021-01-08', '2021-01-15', '2021-01-22',
'2021-01-29', '2021-02-05', '2021-02-12', '2021-02-19',
'2021-02-26', '2021-03-05', '2021-03-12', '2021-03-19',
'2021-03-26', '2021-04-02', '2021-04-09', '2021-04-16',
'2021-04-23', '2021-04-30', '2021-05-07', '2021-05-14',
'2021-05-21', '2021-05-28', '2021-06-04', '2021-06-11',
'2021-06-18', '2021-06-25'],
dtype='datetime64[ns]', freq=None)
代码:
index + pd.DateOffset(hours=2, minutes=10)
输出:
DatetimeIndex(['2021-01-03 02:10:00', '2021-01-10 02:10:00',
'2021-01-17 02:10:00', '2021-01-24 02:10:00',
'2021-01-31 02:10:00', '2021-02-07 02:10:00',
'2021-02-14 02:10:00', '2021-02-21 02:10:00',
'2021-02-28 02:10:00', '2021-03-07 02:10:00',
'2021-03-14 02:10:00', '2021-03-21 02:10:00',
'2021-03-28 02:10:00', '2021-04-04 02:10:00',
'2021-04-11 02:10:00', '2021-04-18 02:10:00',
'2021-04-25 02:10:00', '2021-05-02 02:10:00',
'2021-05-09 02:10:00', '2021-05-16 02:10:00',
'2021-05-23 02:10:00', '2021-05-30 02:10:00',
'2021-06-06 02:10:00', '2021-06-13 02:10:00',
'2021-06-20 02:10:00', '2021-06-27 02:10:00'],
dtype='datetime64[ns]', freq=None)
如果Series
对象或DataFrame
对象使用了DatetimeIndex
类型的索引,此时我们可以通过asfreq()
方法指定一个时间频率来实现对数据的抽样,我们仍然以之前讲过的百度股票数据为例,给大家做一个演示。
原文地址:https://blog.csdn.net/qq_43004728/article/details/138116880
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!