自学内容网 自学内容网

数据分析-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

上面的DataFrameOpenHighLowCloseVolume五个列,分别代表股票的开盘价、最高价、最低价、收盘价和成交量,接下来我们对百度的股票数据进行窗口计算。

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,这是因为在 𝑋 和 𝑌 独立的情况下:

𝐸(𝑋⋅𝑌)=𝐸(𝑋)⋅𝐸(𝑌)=𝜇𝜐

协方差的数值大小取决于变量的大小,通常是不容易解释的,但是正态形式的协方差可以显示两变量线性关系的强弱。在统计学中,皮尔逊积矩相关系数就是正态形式的协方差,它用于度量两个变量 𝑋 和 𝑌 之间的相关程度(线性相关),其值介于-11之间。

𝑐𝑜𝑣(𝑋,𝑌)𝜎𝑋𝜎𝑌

估算样本的协方差和标准差,可以得到样本皮尔逊系数,通常用希腊字母 𝜌 表示。

𝜌=∑𝑖=1𝑛(𝑋𝑖−𝑋¯)(𝑌𝑖−𝑌¯)∑𝑖=1𝑛(𝑋𝑖−𝑋¯)2∑𝑖=1𝑛(𝑌𝑖−𝑌¯)2

我们用 𝜌 值判断指标的相关性时遵循以下两个步骤。

  1. 判断指标间是正相关、负相关,还是不相关。
    • 当 $ \rho \gt 0 $,认为变量之间是正相关,也就是两者的趋势一致。
    • 当 $ \rho \lt 0 $,认为变量之间是负相关,也就是两者的趋势相反。
    • 当 $ \rho \approx 0 $,认为变量之间是不相关的,但并不代表两个指标是统计独立的。
  2. 判断指标间的相关程度。
    • 当 $ \rho $ 的绝对值在 $ [0.6,1] $ 之间,认为变量之间是强相关的。
    • 当 $ \rho $ 的绝对值在 $ [0.1,0.6) $ 之间,认为变量之间是弱相关的。
    • 当 $ \rho $ 的绝对值在 $ [0,0.1) $ 之间,认为变量之间没有相关性。

皮尔逊相关系数适用于:

  1. 两个变量之间是线性关系,都是连续数据。
  2. 两个变量的总体是正态分布,或接近正态的单峰分布。
  3. 两个变量的观测值是成对的,每对观测值之间相互独立。

这里,我们顺便说一下,如果两组变量并不是来自于正态总体的连续值,我们该如何判断相关性呢?对于定序尺度(等级),我们可以使用斯皮尔曼秩相关系数,其计算公式如下所示: 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=1n(n21)6di2 其中, 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,表示计算皮尔逊相关系数;除此之外,还可以指定kendallspearman来计算肯德尔系数或斯皮尔曼秩相关系数。

索引扩展

我们再来看看Index类型,它为SeriesDataFrame对象提供了索引服务,有了索引我们就可以排序数据(sort_index方法)、对齐数据(在运算和合并数据时非常重要)并实现对数据的快速检索(索引运算)。由于DataFrame类型表示的是二维数据,所以它的行和列都有索引,分别是indexcolumnsIndex类型的创建的比较简单,通常给出datadtypename三个参数即可,分别表示作为索引的数据、索引的数据类型和索引的名称。由于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_arraysfrom_productfrom_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()函数来创建日期时间索引,该函数有几个非常重要的参数startendperiodsfreqtz,分别代表起始日期时间、结束日期时间、生成周期、采样频率和时区。我们先来看看如何创建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;你也可以试着将该参数的值修改为12HMQ等,看看会发生什么,相信你不难猜到它们的含义。

输出:

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)!