Python数据分析基础

Python数据分析基础

pandas快速入门

pandas入门

DataFrame 和 Series 简介

pandas是用于数据分析的开源Python库,可以实现数据加载、清洗、转换、统计处理、可视化等功能。
pandas最基本的两种数据结构:
1、DataFrame

  • 可以用来处理结构化数据(SQL数据表、Excel表格)
  • 可以简单理解为一张数据表(带有行标签和列标签)

2、Series

  • 用来处理单列数据,也可以把DataFrame看作由Series对象组成的字典或集合
  • 可以简单理解为数据表的一行或者一列

在这里插入图片描述


数据集(csv和tsv)

csv和tsv文件格式介绍

csv和tsv文件都是存储一个二维表数据的文件类型。
注意:其中csv文件每一列的列元素之间以逗号进行分割,tsv文件每一行的列元素之间以\t进行分割。
在这里插入图片描述


加载数据集(csv和tsv)
# 导入pandas
import pandas as pd

# 加载csv和tsv文件
tips_csv = pd.read_csv('tips.csv')
print(tips_csv)
print(type(tips_csv))           # <class 'pandas.core.frame.DataFrame'>

tips_tsv = pd.read_csv('china.tsv',sep='\t')  #  sep='\t' 以/t分割,默认是逗号
print(tips_tsv)
print(type(tips_tsv))       # <class 'pandas.core.frame.DataFrame'>



DataFrame 的行列标签和行列位置编号

在这里插入图片描述

获取DataFrame的行标签

print(tips_csv.index)       # RangeIndex(start=0, stop=244, step=1)

获取DataFrame的列标签

print(tips_csv.columns)     # Index(['total_bill', 'tip', 'sex', 'smoker', 'day', 'time', 'size'], dtype='object')

可以单独设置某一列为行标签

# 设置DataFrame的行标签
df_tsv = tips_csv.set_index("year")

注意:单独设置行标签不会改变原来的DataFrame。
在这里插入图片描述行位置编号:从上到下,从0开始到n-1
列位置编号:从左到右,从0开始到n-1


DataFrame 获取指定行列的数据

loc方法

在这里插入图片描述

# loc函数 获取指定行列的数据
# 获取行标签为1952,1962,1972,列标签为country,pop,gdpPercap的数据
data1 = df_tsv.loc[[1952,1962,1972],['country','pop','gdpPercap']]
# 获取行标签为1952,1962,1972的全部数据
data2 = df_tsv.loc[[1952,1962,1972]]
# 获取列标签为country,pop,gdpPercap的全部数据
data3 = df_tsv.loc[:,['country','pop','gdpPercap']]     # 所有行前面要加:
# 获取行标签为1952的全部数据
data4 = df_tsv.loc[[1952]]      # dataframe类型
data5 = df_tsv.loc[1952]        # series类型



iloc方法

根据位置编号
在这里插入图片描述

# iloc函数 获取指定行列的数据
# 获取行位置为0、2、4,列位置为0、1、2的数据
data6 = df_tsv.iloc[[0,2,4],[0,1,2]]
# 获取行位置为0、2、4的数据
data7 = df_tsv.iloc[[0,2,4]]
# 获取列位置为0、1、2的数据
data8 = df_tsv.iloc[:,[0,1,2]]
# 获取行位置为1的数据
data9 = df_tsv.iloc[1]
# 获取行位置为1,列位置为2的数据
data10 = df_tsv.iloc[1,2]



[ ]方法

在这里插入图片描述

# []函数 获取指定行列的数据
# 获取列标签为country,pop,gdpPercap的全部数据
data11 = df_tsv[['country','pop','gdpPercap']]
# 获取列标签为popp的全部数据
data12 = df_tsv['pop']      # series类型
data13 = df_tsv[['pop']]      # dataframe类型
# 获取前三行数据
data14 = df_tsv[0:3]
# 从第一行开始,每隔一行获取一行数据,一共获取三行
data15 = df_tsv[0:5:2]



loc和iloc的切片操作

在这里插入图片描述

# loc和iloc的切片操作
data_split1 = df_tsv.loc[1952:1962,'country':'lifeExp']
data_split2 = df_tsv.iloc[0:3,0:3]          # iloc结束的结束行列位置不包括在内

注意:需要确保行索引是唯一的,否则 Pandas 无法正确处理切片操作。
可以下列语句查询索引是否唯一:

print(df_tsv.index.is_unique)

如果返回 False,说明索引中有重复值。

下面有两种方法可以解决:

  1. 重置索引。

    df_tsv.reset_index(inplace=True)
    

    然后可以进行条件过滤

    data_split1 = df_tsv[(df_tsv['year'] >= 1952) & (df_tsv['year'] <= 1962)][['country', 'lifeExp']]
    
  2. 去重处理

    df_tsv = df_tsv[~df_tsv.index.duplicated(keep='first')]
    
    • keep=‘first’:保留第一个重复值,丢弃后续的。
    • keep=‘last’:保留最后一个重复值,丢弃前面的。
  3. 改用条件过滤

    data_split1 = df_tsv[(df_tsv['year'] >= 1952) & (df_tsv['year'] <= 1962)][['country', 'lifeExp']]
    



Series详解

series是pandas中用来存储一堆数据的容器。


创建Series

创建series最简单的方式是传入一个python列表

  • 如果传入的数据是一个统一的数字,那么最终的类型是int64
  • 如果传入的数据是统一的字符串,那么最终的类型是object
  • 如果传入的数据是混合类型,那么最终的类型是object
# 创建series
a = pd.Series(['ban',10])
print(a)            # dtype: object
print(type(a))
b = pd.Series(['ba','as'])
print(b)            # dtype: object
print(type(b))
c = pd.Series([4,10])
print(c)            # dtype: int64
print(type(c))

创建series时,也可以通过index参数来指定行标签

# 创建series时,也可以通过index参数来指定行标签
s = pd.Series(['s',1],index=['name','age'])
print(s)



Series常用属性操作

在这里插入图片描述

# series常用操作
scientists = pd.read_csv('scientists.csv')
age = scientists['Age']
print(age.shape)            # (8,)
print(age.size)             # 8
print(age.index)            # RangeIndex(start=0, stop=8, step=1)
print(age.values)           # [37 61 90 66 56 45 41 77]
print(age.keys())           # RangeIndex(start=0, stop=8, step=1)
print(age.loc[1])           # 61
print(age.iloc[1])          # 61
print(age.dtype)            # int64



Series常用统计方法

在这里插入图片描述
在这里插入图片描述

# series 常用统计方法
print(age.mean())             # 均值 59.125000
print(age.max())              # 最大值 90.000000
print(age.min())              # 最小值 37.000000
print(age.std())              # 标准差 18.325918
print(age.value_counts())   # 统计不同元素的个数 8
print(age.count())          # 统计非空元素的个数 7
print(age.describe())       # 显示各种统计值



Series 的bool索引

series支持bool索引,可以从series获取bool索引为True的位置的对应数据。

# series的bool索引
bool_value = [False,True,True,False,False,False,True,False]
print(age[bool_value])          # 取出True对应位置的值

# 应用:筛选出大于平均年龄的人
age_over = age[age > age.mean()]        # 括号内就是一个bool值
print(age_over)



Series 运算

在这里插入图片描述

DataFrame

创建DataFrame

可以用字典创建DataFrame

import pandas as pd
peoples = pd.DataFrame({
    'name' : ['zmx','hh'],
    'age' : [18,22],
})

在这里插入图片描述


创建DataFrame时可以用colums参数指定列的顺序,也可以使用index参数来指定行标签

peoples = pd.DataFrame({
    'name' : ['zmx','hh'],
    'age' : [18,22],
}, columns = ['age','name'], index=['smart','david'])

在这里插入图片描述

也可以使用嵌套列表创建DataFrame,并使用columns参数指定列标签,使用index参数来指定行标签

peoples = pd.DataFrame([
    ['zmx','hh'],
    [18,22],
], columns = ['age','name'], index=['smart','david'])

在这里插入图片描述


DataFrame常用属性操作

在这里插入图片描述

scientists = pd.read_csv('scientists.csv')
print(scientists.shape)             # (8, 5)
print(scientists.size)              # 总元素的个数 40
print(scientists.ndim)              # 数据的维度 2
print(len(scientists))              # 数据的行数 8
print(scientists.index)             # 行标签 RangeIndex(start=0, stop=8, step=1)
print(scientists.columns)           # 列标签 Index(['Name', 'Born', 'Died', 'Age', 'Occupation'], dtype='object')
print(scientists.dtypes)            # 每列数据元素的类型 
print(scientists.info())              # 每列的结构
print(scientists.head(2))           # 获取前2行数据,默认为5
print(scientists.tail(2))           # 获取最后2行数据,默认为5



DataFrame常用统计方法

在这里插入图片描述


DataFrame的bool索引

series支持bool索引,可以从series获取bool索引为True的位置的对应的行数据。

# DataFrame的bool索引
bool_peop = scientists[scientists['Age'] > scientists['Age'].mean()]
print(bool_peop)

结果:
在这里插入图片描述


DataFrame的运算

在这里插入图片描述


DataFrame行标签和列标签的操作

重新指定行标签

加载数据文件时,如果不指定行标签,pandas会自动加上从0开始的行标签。
可以通过df.set_index(‘列名’)的方法重新将指定的列数据设置为行标签;也可以通过df.reset_index()来重置行标签。

scientists_df = scientists.set_index('Name')
scientists_df.reset_index()         # 重置行标签



加载数据时,直接指定行标签
scientists1 = pd.read_csv('scientists.csv',index_col='Name')



加载数据后,修改行列标签

在这里插入图片描述
注意:直接修改的话行列标签的数量必须和原来的时一致的。

# 加载数据后,修改行列标签
index_name = {'Rosaline Franklin':'rosaline franklin'}
column_name = {'Born':'born'}
scientists_df_change = scientists_df.rename(index=index_name,columns=column_name)

在这里插入图片描述


DataFrame增删改

行操作

添加行

添加行时,会返回新的DataFrame

df._append(other,ignore_index=True)
# 添加行
new_series = pd.Series(['LuoGeng Hua','1910-11-12','1985-06-12',75,'Mathemattician'],
          index=['Name','Born','Died','Age','Occupation'])          # 创建新的series
new_scientists = scientists._append(new_series,ignore_index=True)



修改行

删除行时是直接对原来的DataFrame进行修改
在这里插入图片描述

# 修改行
# 修改行标签为4的所有数据
scientists.loc[4] = ['Rachel Carson','1907-5-27','1964-4-14',75,'Biologist']
# 修改行标签为4 的行的Born和Age列的数据
scientists.loc[4,['Born','Age']]=['1906-5-27',58]



删除行

删除行时,会返回新的DataFrame

df.drop[行标签]
# 删除行
# 删除行标签为1和3 的行
scientists.drop([1,3])



列操作

新增列/修改列

在这里插入图片描述

# 新增一个Country列
scientists['Country'] = ['England','England','England','French',
                         'American','England','England','Germany']
"""
或者
scientists.loc[:,'Country'] = ['England','England','England','French',
                         'American','England','England','Germany']
"""
# 修改Country列中的数据
scientists['Country'] = ['england','england','england','french',
                         'american','england','england','germany']
"""
或
scientists[:,'Country'] = ['england','england','england','french',
                         'american','england','england','germany']
"""



删除列

删除列时,会返回新的DataFrame
在这里插入图片描述

# 删除列
drop_scientists_lie = scientists.drop(['Country'],axis=1)       # axis默认为0,代表删除行



DataFrame 数据导出和导入

Pickle文件

在这里插入图片描述

# Pickle文件
scientists = pd.read_csv('scientists.csv')
scientists.to_pickle('scientists_df.pickle')
# 读取pickle文件
scientists_df = pd.read_pickle('scientists_df.pickle')



CSV文件

在这里插入图片描述

# CSV文件
# tsv文件,设置分隔符必须为\t
scientists.to_csv('scientists_df.tsv',sep='\t')
# 不在csv文件中存入行标签
scientists.to_csv('scientists_df.tsv',index=False)



Excel文件

在这里插入图片描述

# Excel文件
scientists.to_excel('scientists_df.xlsx',sheet_name='scientists',index=False)
# 读取excel文件
pd.read_excel('scientists_df.xlsx')



其它

在这里插入图片描述
feather文件
在这里插入图片描述

DataFrame 查询

条件查询

在这里插入图片描述

# 条件查询
# 获取Age在60-80之间的科学家信息
age_6080 = scientists.loc[(scientists['Age']>60)&(scientists['Age']<80)]
"""
age_6080 = scientists.loc[(scientists.Age>60)&(scientists.Age<80)]
"""
age_60802 = scientists.query('Age > 60 & Age < 80')



分组聚合

在这里插入图片描述
在这里插入图片描述

# 分组聚合
# 按照Occupation职业分组,并计算每组年龄的平均值
g1 = scientists.groupby('Occupation')['Age'].mean()
# 按照Occupation职业分组,并计算每组的人数和年龄的平均值
g2 = scientists.groupby('Occupation').agg({'Name':'count','Age':'mean'})



排序

在这里插入图片描述

# 按年龄排序,降序
s1 = scientists.sort_values('Age',ascending=False)
# 按行标签排序,降序
s2 = scientists.sort_index(ascending=False)

补充:Series也可以排序,但是Series的sort_values方法没有by参数。

# 按照series数据的值进行排序
s3 = scientists['Age'].sort_values()
# 按照series数据的行标签进行排序,降序
s4 = scientists['Age'].sort_index(ascending=False)



nlargest 和 nsmallest 函数

在这里插入图片描述

# nlargest 和 nsmallest 函数
# 获取Age最大的前三行数据
a1 = scientists.nlargest(3,columns='Age')  
# 获取Age最小的前三行数据
a2 = scientists.nsmallest(3,columns='Age')



基本绘图

from matplotlib import pyplot as plt
# 绘图
# 将科学家按职业分组,计算平均寿命并绘图
scientists_groupby_occ = scientists.groupby('Occupation')['Age'].mean()
scientists_groupby_occ.plot(figsize=(20,8))
plt.show()

# 绘制柱状图
plt.rcParams['font.sans-serif'] = 'SimHei'   # 设置显示中文,mac: plt.rcParams['font.san-serif'] = 'Arial Unicode MS'
scientists_groupby_occ.plot.bar(figsize=(20,8))
plt.show()



租房数据案例分析

import pandas as pd
import numpy as np

# 加载csv数据
house_data = pd.read_csv('LJdata.csv')
# print(house_data.head() )  # 查看前五行数据
# print(house_data.shape)    # (2760, 13) 有2760条数据,13行
# print(house_data.columns)  # 查看列标题

# 重新设置列标签
house_data.columns = ['district','address','title','house_type','area',
                      'price','floor','build_time','direction','update_time',
                      'view_num','extra_info','link']

# 完整显示describe的全部信息不省略
pd.set_option('display.max_rows', None)
pd.set_option('display.max_columns', None)

# print(house_data.describe())    # 查看每列统计值
print(house_data.describe(include=[np.object_]).T )  # 显示object列的统计值  .T:行列倒转  或者include='all'直接显示全部列

"""
1、查看房屋租金的最高价格、最低价格、平均值和中位数
2、找到租金最低和最高的房子的全部信息
3、查看看房人数最多的前20个房子的区域
4、查看看房人数最多的房屋的朝向
5、查看出租房屋的户型分布情况
6、查看热门小区(看房人数top5)
7、查看出租放源最多的小区
8、查看望京租房价格在4000以下的房屋信息
9、查看租房价格在2000以下的房屋信息,并按照价格从低到高排序
"""
# 1、查看房屋租金的最高价格、最低价格、平均值和中位数
describe = house_data.describe()
# print(describe)

# 2、找到租金最低和最高的房子的全部信息
house_max_price = house_data.loc[house_data['price'] == house_data['price'].max()]
# print(house_max_price)
house_low_price = house_data.loc[house_data['price'] == house_data['price'].min()]
# print(house_low_price)

# 3、查看看房人数最多的前20个房子的区域
sort_by_view_20 = house_data.groupby('district')['view_num'].sum().sort_values(ascending=False).head(20)
# print(sort_by_view_20)

# # 绘制柱状图
# plt.rcParams['font.sans-serif'] = 'SimHei'   # 设置显示中文,mac: plt.rcParams['font.san-serif'] = 'Arial Unicode MS'
# sort_by_view_20.plot.bar(figsize=(20,8))
# plt.show()

# 4、查看看房人数最多的房屋的朝向
house_max_view = house_data.groupby('direction')['view_num'].sum().sort_values(ascending=False).head(1)
# print(house_max_view)

# 5、查看出租房屋的户型分布情况
house_rent = house_data.loc[house_data['price'] != 0]
# house_data.groupby('housetype')['title'].count().sort_values(ascending=False)
# print(house_rent['house_type'])

# 6、查看热门小区(看房人数top5)
sort_by_view_5 = house_data.groupby('address')['view_num'].sum().nlargest(5)
# print(sort_by_view_5)

# 7、查看出租房源最多的小区
community_max_rent = house_data.groupby('address')['price'].count().nlargest(1)
# print(community_max_rent)

# 8、查看望京租房价格在4000以下的房屋信息
house_price_low_2000 = house_data.loc[(house_data['price'] < 4000) & (house_data['district'] == '望京租房')]
# a2 = house_data.query('district == '望京租房' & price <= 4000')
# print(house_price_low_2000)

# 9、查看租房价格在2000以下的房屋信息,并按照价格从低到高排序
sort_by_house_price_low_2000_desc = house_data.query('price <= 2000').sort_values('price')
print(sort_by_house_price_low_2000_desc)



pandas 数据清洗

数据组合

在动手进行数据分析工作之前,需要进行数据清理工作,数据清理的主要目标是:

  • 每个观测值成一行
  • 每个变量成一列
  • 每种观测单元构成一张表格

数据整理好之后,可能需要多张表格组合到一起才能进行某些问题的分析。


concat 拼接数据

在这里插入图片描述


行拼接:按照列标签索引对齐

注意:行拼接时,无法对齐的列,默认值设为NaN

Dadaframe 与 Dadaframe 行拼接
# 行拼接
df_concat0 = pd.concat([df1,df2,df3])    # 行拼接,按列对齐
# print(df_concat)

df_concat = pd.concat([df1,df2,df3],ignore_index=True)  # 忽略原来的行标签,从0开始重新排序
# print(df_concat)



Dadaframe 与 Series 行拼接
# Dadaframe 与 Series 行拼接
new_series = pd.Series(['n1','n2','n3','n4'])   # 创建一个新series
# print(new_series)
# 将df1 与 new_series 按照列标签对齐,进行拼接
concat1 = pd.concat([df1,new_series])
print(concat1)

在这里插入图片描述


列拼接:按照列行签索引对齐

注意:列拼接时,无法对齐的行,默认值设为NaN


Dadaframe 与 Dadaframe 列拼接
# Dadaframe 与 Dadaframe 列拼接
concat2 = pd.concat([df1,df2,df3],axis=1)
print(concat2)

在这里插入图片描述


Dadaframe 与 Series 列拼接
# Dadaframe 与 Series 列拼接
concat3 = pd.concat([df1,new_series],axis=1)
print(concat3)

在这里插入图片描述


concat 方法的join函数

concat方法的join函数:

  • 默认为outer:无法对齐的行列,默认填充值为NaN。
  • 设置为inner:只有能够对齐的行业,才会出现在拼接的结果中。
# join 函数的设置
# 修改df1 和 df3 的columns列标签
df1.columns = ['A','B','C',"D"]
df3.columns = ['A','C','F','H']
# 行拼接
concat4 = pd.concat([df1,df3],join='outer')
print(concat4)

在这里插入图片描述

concat5 = pd.concat([df1,df3],join='inner')
print(concat5)

在这里插入图片描述


merge 关联数据

基本格式

merge方法类似sql中的join语句,用于两个数据集之间按照行列标签索引连接,默认时inner,可以设置为:left、right、outer。
在这里插入图片描述
在这里插入图片描述


merge 示例

从sqlite数据库中加载table数据

import pandas as pd
from sqlalchemy import create_engine
"""
sqlite 是一个微型的、本地的数据库,不必像mysql一样需要额外启动,
依靠其特殊的文件,用来存储数据,并提供和数据文件交互的方法。
"""
# 创建数据库连接对象,并指定数据文件路径
engine = create_engine('sqlite:///chinook.db')
# 加载其中的tracks数据表的数据
tracks = pd.read_sql('tracks',engine)

# 完整显示describe的全部信息不省略
pd.set_option('display.max_rows', None)
pd.set_option('display.max_columns', None)

# 查看前五个数据
print(tracks.head())

# 加载其中的genres数据表的数据
genres = pd.read_sql_table('tracks',engine)
# 查看前五个数据
# print(genres.head())

# 将genres和tracks按照GenreId进行merge操作,设置为left连接
tracks_sub = tracks[['TrackId','GenreId','Milliseconds']]
genres_track = genres.merge(tracks_sub, on='GenreId', how='left')
# print(genres_track)


# 计算不同类型的音乐的平均时长
genre_time = genres_track.groupby('Name')['Milliseconds_y'].mean()
# print(avg_time)

# 将genre_time数据从ms 转换为时间单位,保留到s
genre_time2 = pd.to_timedelta(genre_time,unit='ms').dt.floor('s').sort_values(ascending=False)
print(genre_time2)



join 关联数据

基本格式

join方法类是merge方法的一个特殊情况,被调用的数据集按照行列标签索引和另一个数据集的行标签索引关联,默认是left,可以设置为:right、inner、outer。
在这里插入图片描述


join 示例

import pandas as pd

# 加载数据集
stock_2016 = pd.read_csv('stocks_2016.csv')
stock_2017 = pd.read_csv('stocks_2017.csv')
stock_2018 = pd.read_csv('stocks_2018.csv')

# stock_2016 和 stock_2017 按照行标签进行关联,设置为outer连接
join1 = stock_2016.join(stock_2017,lsuffix='2016',rsuffix='2017',how='outer')
# print(join1)

# stock_2016 和 stock_2018 按照Symbol 进行关联
join2 = stock_2016.join(stock_2018.set_index('Symbol'),on='Symbol',lsuffix='2016',rsuffix='2018')
print(join2)



merge与join差别

在这里插入图片描述
一般情况下优先使用merge方法。


缺失值

缺失值NaN

在这里插入图片描述
注意:缺失值和其它类型的数据不同,它毫无意义,NaN不等于0,也不等于空字符串,两个NaN也不相等
在这里插入图片描述


加载缺失值

import pandas as pd
df1 = pd.read_csv('survey_visited.csv',keep_default_na=False)   # 缺失值未被填充为NaN
df2 = pd.read_csv('survey_visited.csv',na_values='DR-3')    # 把DR-3设置为缺失值



缺失值查看

import pandas as pd
df1 = pd.read_csv('survey_visited.csv',keep_default_na=False)   # 缺失值未被填充为NaN
df2 = pd.read_csv('survey_visited.csv',na_values='DR-3')    # 把DR-3设置为缺失值

# 缺失值查看
titanic = pd.read_csv('titanic.csv')
# print(titanic.info())


# 构造缺失值统计的函数
def missing_values_table(df):
    # 计算所有缺失值
    mis_val = df.isnull().sum()

    # 计算缺失值的比列
    mis_val_percent = 100 * mis_val / len(df)

    # 将结果拼接成df
    mis_val_table = pd.concat([mis_val,mis_val_percent],axis=1)

    # 将列重命名
    mis_val_table_ren_columns = mis_val_table.rename(columns={0:'缺失值',1:'占比(%)'})

    # 将缺失值为0的列去除,并按缺失值占比排序
    mis_val_table_ren_columns = mis_val_table_ren_columns[mis_val_table_ren_columns['缺失值'] != 0].sort_values('占比(%)')

    # 打印信息
    print(f'传入的数据集共{df.shape[1]}列,\n其中{mis_val_table_ren_columns.shape[0]}列有缺失值')

    return mis_val_table_ren_columns

titanic_df = missing_values_table(titanic)
print(titanic_df)



使用Missingno库对缺失值的情况进行可视化探查

安装Missingno

pip install Missingno
# 使用Missingno库对缺失值的情况进行可视化探查
import missingno as msno
import matplotlib.pyplot as plt
msno.bar(titanic)
plt.show()

在这里插入图片描述

# 查看缺失值的分布情况
msno.matrix(titanic)
plt.show()

在这里插入图片描述

# 查看缺失值之间的相关性
msno.heatmap(titanic)
plt.show()

在这里插入图片描述


缺失值处理

删除缺失值

注意:删除缺失值会损失信息,并不推荐删除,当缺失值占比较高的时候,可以尝试使用删除缺失值。


按行删除缺失值
# 按行删除
# 复制一份数据
titanic_cp = titanic.copy()
# 对Age列进行处理,空值就删除整行数据
titanic_cp.dropna(subset=['age'],how='any',inplace=True)	# inplace=True 直接在原数据集进行修改,默认为False,生成新数据集
# 输出Age列缺失值的总数
print(titanic_cp['age'].isnull().sum())
# 图形化缺失值情况
msno.matrix(titanic_cp)
plt.show()

在这里插入图片描述


按列删除缺失值
# 按列删除
titanic_cp2 = titanic.copy()
# 对age列进行处理,空值就删除整列数据
titanic_cp2.drop(['age'],axis=1,inplace=True)
# 图形化
msno.matrix(titanic_cp2)
plt.show()



填充缺失值(非时间序列)

使用常量进行填充
# 使用常量进行填充
# 复制一份数据
titanic_cp3 = titanic.copy()
# 计算各列空值总数
a = titanic_cp3.isnull().sum()
# print(a)

# 将空值都填为0,inplace=True 为必要参数
titanic_cp3.fillna(0,inplace=True)

# 计算各列空值总数
print(titanic_cp3.isnull().sum())



使用统计量进行填充
# 使用统计量进行填充
# 复制一份数据
titanic_cp4 = titanic.copy()
# 计算age的平均值
age_mean = titanic_cp4['age'].mean()

# 将空值都填为0,inplace=True 为必要参数
titanic_cp4.fillna({'age': age_mean},inplace=True)

# 计算各列空值总数
print(titanic_cp4.isnull().sum())



填充缺失值(时间序列)

时间序列在某一列值的变化往往有一定线性规律,绝大多数的时序数据,具体的列值随着时间的变化而变化,所以对于有时序的行数据缺失值处理有三种方式:

  • 前项填充:用时间序列中空值的上一个非空值填充
  • 后项填充:用时间序列中空值的下一个非空值填充
  • 线性填充:线性插值方法
# 加载数据集
city_day = pd.read_csv('city_day.csv',index_col='Date',parse_dates=True)    # parse_dates=True 转换成日期类型
# 复制一份数据
city_day_cp1 = city_day.copy()
city_day_cp2 = city_day.copy()
city_day_cp3 = city_day.copy()
# 查看数据的前五行
# print(city_day_cp1.head())
# 查看缺失值情况
city_day_missing = missing_values_table(city_day_cp1)
# print(city_day_missing)



前项填充
# 用时间序列中空值的上一个非空值填充
# 填充缺失值
city_day_cp1.ffill(inplace=True)
# 截取一小段数据查看填充情况
print(city_day_cp1['Xylene'][50:64])

在这里插入图片描述

后项填充
# 用时间序列中空值的下一个非空值填充
# 填充缺失值
city_day_cp2.bfill(inplace=True)
# 截取一小段数据查看填充情况
print(city_day_cp2['Xylene'][50:64])

在这里插入图片描述

线性填充

在这里插入图片描述

# 线性填充
# 填充缺失值
city_day_cp3.interpolate(limit_direction='both',inplace=True)
# 截取一小段数据查看填充情况
print(city_day_cp3['Xylene'][50:64])

在这里插入图片描述

数据整理

wide_to_long 整理数据

wide_to_long函数的作用是将列名起始部分相同的列进行拆解,使宽数据变换为长数据(变成多行)。

import pandas as pd
# 加载数据集
movies = pd.read_csv('movie.csv')
# 取出需要用到的列
movies_actors = movies[['movie_title','actor_1_name','actor_2_name','actor_3_name',
                        'actor_1_facebook_likes','actor_2_facebook_likes','actor_1_facebook_likes']]
# 修改列标签,将列名起始部分变成相同的部分
movies_actors.columns = ['movie_title','actor_name_1','actor_name_2','actor_name_3',
                         'actor_facebook_likes_1','actor_facebook_likes_2','actor_facebook_likes_3']


# wide_to_long 使用
a =  pd.wide_to_long(movies_actors,
            stubnames=['actor_name','actor_facebokk_like'],
            i=['movie_title'],
            j='actor_num',
            sep='_'
        ).reset_index()     # 重置行标签
print(a)



melt 整理数据

在这里插入图片描述


宽数据集变成长数据集
# melt 整理数据
pew = pd.read_csv('pew.csv')
# print(pew)
# 宽数据集变成长数据集
pew_long = pd.melt(pew,id_vars=['religion'])
# print(pew_long)
print(pew_long.loc[pew_long['religion'] == 'Agnostic'])

在这里插入图片描述

# 练习
# 将billboard拆分成两个数据集,一个保留歌曲的基本信息,一个保存歌曲的每周排行信息,要求两个数据集以一个id列,在逻辑上相互关联
billboard = pd.read_csv('billboard.csv')
billboard_long = pd.melt(billboard,id_vars=['year','artist','track','time','date.entered'],
                         var_name = 'week',
                         value_name = 'rating'
                         )
# print(billboard_long)
# 保存歌曲信息
billboard_songs = billboard_long[['year','artist','track','time','date.entered']]
billboard_songs = billboard_songs.drop_duplicates() # 去重
billboard_songs['id'] = range(len(billboard_songs)) # 添加id列
# print(billboard_songs)

# 原长数据集与歌曲信息表进行merge合并,基于指定的字段,这样歌曲id就放到了长数据集中
new_billboard_long = billboard_long.merge(billboard_songs,
                                          on=['year','artist','track','time','date.entered'])
# print(new_billboard_long)

# 保存歌曲的每周排行信息
billboard_week = new_billboard_long[['id','week','rating']]
print(billboard_week)



stack 整理数据

stack 和 unstack 简介

pandas进行数据重排时,经常用到stack 和 unstack两个函数。stack的意思是堆叠,unstack的意思是不要堆叠。
在这里插入图片描述
stack函数会将数据从表格结构变成花括号结构(返回的是series类型),即将其列索引变成行索引;反之,unstack函数将数据从花括号结构变成表格结构,即将其中一层的行索引变成列索引。


stack 功能演示
# stack 整理数据
state_fruit = pd.read_csv('state_fruit.csv')
# 将数据从表格结构变成花括号结构
state_fruit_series = state_fruit.stack()
print(state_fruit_series)

在这里插入图片描述

# 使用reset_index(),结果会变回DataFrame
state_fruit_tidy = state_fruit_series.reset_index()
state_fruit_tidy.columns = ['state','apple','weight']
# print(state_fruit_tidy)

# 也可以用rename_axis给不同的行标签索引层级命名
state_fruit_series2 = state_fruit.stack().rename_axis(['state','fruit'])
# print(state_fruit_series2)
# 使用reset_index(),结果会变回DataFrame
state_fruit_tidy2 = state_fruit_series2.reset_index()
# print(state_fruit_tidy2)

数据类型

Numpy介绍

Numpy是一个开源的Python科学计算库,用于快速处理任意维度的数组。
ndarry比Python原生list的运算效率更高。
在这里插入图片描述

import numpy as np
# 创建ndarray
score = np.array([
    [80,89,86,67,79],
    [78,97,89,67,82],
    [90,94,78,67,74]
])
print(score)
print(type(score))  # <class 'numpy.ndarray'>



Numpy的ndarray

ndarray 的 属性

在这里插入图片描述

ndarray 的类型

在这里插入图片描述
创建ndarry的时候指定类型:

b = np.array([[1,2,3],[4,5,6]],dtype = np.float32)



pandas 的数据类型

pandas是基于Numpy的,很多功能都依赖于Numpy的ndarray实现的,pandas的数据类型很多与Numpy类型,属性也有很多类似。
在这里插入图片描述

类型转换

astype函数

astype方法是通过函数,把DataFrame中的任何列转换为其它dtype,可以向astype方法提供任何内置类型或numpy类型来转换列的数据类型。

astype转换为字符串对象

# 类型转换
tips['sex_str'] = tips['sex'].astype(str)
print(tips.dtypes)   # 查看列的类型

结果:出现了新的一列sex_str
在这里插入图片描述

to_numeric函数

在这里插入图片描述

# to_numeric函数
tips_sub_miss = tips.head(10)   # 取前十行数据
tips_sub_miss.loc[[1,3,5,7],'total_bill'] = 'missing'   # 人为制造missing数据
# print(tips_sub_miss)  # 此时total_bill变成了字符串类型
try:
    tips_sub_miss['total_bill'] = tips_sub_miss['total_bill'].astype(float)
    print(tips_sub_miss)
except Exception as e:
    print("出现异常")   # 报错
tips_sub_miss['total_bill'] = pd.to_numeric(tips_sub_miss['total_bill'],errors='coerce',downcast='float')
print(tips_sub_miss.dtypes)

在这里插入图片描述
在这里插入图片描述

分类数据类型 category

category的创建

category类型数据是由固定的且有限数量的变量组成的,比如:性别。

# 通过pd.Categorical创建category类型数据,同时指定可选项
s = pd.Series(
    pd.Categorical(
        ['a','b','c','d'],
        categories=['c','b','a']    # 指定category的取值,除取值外都为NaN
    )
)
print(s)

a = pd.Series(['a','b','c','d'],dtype='category')
print(a)

在这里插入图片描述


category的排序
# 排序
print(a.sort_values())

在这里插入图片描述

我们也可以自定义数据的大小关系

# 自定义顺序
from pandas.api.types import CategoricalDtype
b = CategoricalDtype(categories=['B','D','A','C'],ordered=True) # 相当于一种类型
a = a.astype(b)
print(a.sort_values())

在这里插入图片描述
我们也可以重新修改排序规则

# 临时改变排序规则
a = a.cat.reorder_categories(['D','B','A','C'],ordered=True)
print(a.sort_values())

在这里插入图片描述


pandas 数据处理

apply 自定义函数

apply函数简介

pandas的apply()函数可以作用于Series或者整个DataFrame,功能也是自动遍历整个Series或者DataFrame,对每一个元素运行指定的函数。

  • pandas提供了很多数据处理的API,但当提供的API不能满足需求的时候,需要自己编写数据处理函数,这个时候可以使用apply函数。
  • apply函数可以接受一个自定义函数,可以将DataFrame的行或者列数据传递给自定义函数处理
  • apply函数类似于编写一个for循环,遍历行列的每一个元素,但比使用for循环高效很多。


Series的apply方法

Series有一个apply方法,该方法有一个func参数,当传入一个函数后,apply方法就会把传入的函数应用于每个元素。

# Series的apply方法
df = pd.DataFrame({'a': [10, 20, 30], 'b': [20, 30, 40]})
# print(df)

# 自定义一个函数
def my_sq(x):
    return x ** 2

x = df['a'].apply(my_sq)
print(x)

apply也可以接受多参数

# apply接受多参数
def my_add(x,n):
    return x + n

print(df['a'].apply(my_add,args=(3,)))  # 注意:args参数必须是一个元组,这里的3是传递给n的
print(df['a'].apply(my_add,n=1))    # 也可以直接制定n



DataFrame的apply方法

### DataFrame的apply方法
def sub_one(x):
    return x - 1
print(df.apply(sub_one))	# 针对df进行apply操作,默认按列执行

print(df.apply(sub_one,axis=1)) # 设置为按行执行



DataFrame的applymap方法

DataFrame还有一个applymap函数,applymap也有一个func参数接受一个函数,针对DataFrame每个值应用func指定的函数进行操作,分别返回的结果构成新的DataFrame对象
注意:applymap是DataFrame独有的,Series没有。

print(df.applymap(sub_one))



apply函数的使用案例

# apply函数的使用案例

# 加载数据集
titanic = pd.read_csv('titanic.csv')
print(titanic.info()) # 查看基本信息(缺失值)

# 需求:用apply计算每列缺失值的个数
def count_missing(vec):
    return vec.isnull().sum()
    pass
print(titanic.apply(count_missing))

# 需求:计算每列缺失值的比例
def missing_perc(x):
    return x.isnull().sum()/len(x)
print(titanic.apply(missing_perc))

# 需求:计算非缺失值的比例
def notmissing_perc(x):
    return 1 - x.isnull().sum()/len(x)
print(df.apply(notmissing_perc))

# 需求: 计算每行缺失值的个数
def count_missing_row(vec):
    return vec.isnull().sum()
print(titanic.apply(count_missing,axis=1)) # 按行



函数向量化

@np.vectorize # 函数向量化

# 函数向量化
df2 = pd.DataFrame(
    {
        'a':[10,20,30],
        'b':[40,50,60]
    }
)

# 计算两个Series求和
def add_vec(x,y):
    return x + y

print(add_vec(df2['a'],df2['b']))

# 稍微修改一下函数
def add_vec_2(x,y):
    if x != 0:
        return x + y
    else:
        return x
# print(add_vec_2(df2['a'],df2['b'])) # 报错,x是向量,x != 0不能判断
# 此时我们使用@np.vectorize,将函数向量化
@np.vectorize   # 函数向量化
def add_vec_2(x,y):
    if x != 0:
        return x + y
    else:
        return x
print(add_vec_2(df2['a'],df2['b'])) # 报错,x是向量,x != 0不能判断



lambda函数

使用apply或者applymap对数据进行处理时,当条件比较简单时,没有必要创建一个函数,可以使用lambda表达式创建匿名函数。

# lambda函数
print(df2.apply(lambda x : x + 1))



数据分组操作

分组聚合简介

在SQL中我们经常使用group by 将某个字段按照不同的取值进行分组,在pandas中也有group by 函数;分组之后,每组都会有至少1一条数据,将这些数据进一步处理返回单个值的过程就是聚合。
比如:分组之后计算算术平均数或者分组之后计算频数,都属于聚合。
在这里插入图片描述
注意:方式1 只能使用pandas的内置的聚合方式,并且只能进行一种聚合;方式2和3除了能够使用pandas内置的聚合,还可以使用其它聚合方法,并且可以进行多种聚合。


pandas内置的聚合方法

在这里插入图片描述

分组聚合示例

import pandas as pd
import numpy as np

gapminder = pd.read_csv('gapminder.tsv',sep='\t')
# print(gapminder.head())

"""
1、计算每年期望年龄的平均值
2、统计每年预期寿命的最小值、最大值和平均值
3、统计每年的人均寿命和GDP的最大值
4、计算每年期望年龄的平均值(自定义聚合函数)
5、统计每年的平均年龄和所有平均年龄的插值(自定义聚合函数)
"""
# 1、计算每年期望年龄的平均值
a1 = gapminder.groupby('year').lifeExp.mean()
# a1 = gapminder.groupby('year').agg({'lifeExp':'mean'})
# a1 = gapminder.groupby('year').agg({'lifeExp': np.mean})
# print(a1)

# 2、统计每年预期寿命的最小值、最大值和平均值
a2 = gapminder.groupby('year').lifeExp.agg(['min','max','mean'])
# a2 = gapminder.groupby('year').agg({'lifeExp':['min','max','mean']})
# print(a2)

# 3、统计每年的人均寿命和GDP的最大值
a3 = gapminder.groupby('year').agg({'lifeExp' : 'mean' , 'gdpPercap' : 'max'})
# print(a3)

# 4、计算每年期望年龄的平均值(自定义聚合函数)
def age_mean(vec):
    n = len(vec)
    age_sum = sum(vec)
    return age_sum / n

a4 = gapminder.groupby('year').lifeExp.agg(age_mean)
# print(a4)

# 5、统计每年的平均年龄和所有平均年龄的差值(自定义聚合函数)
global_mean = gapminder['lifeExp'].mean()
def diff_lifeExp(vec,global_mean):
    return vec.mean() - global_mean

a5 = gapminder.groupby('year').lifeExp.agg(diff_lifeExp,global_mean = global_mean)
print(a5)



分组transform 转换

transform转换需要把DateFrame中的值传递给一个函数,然后由该函数“转换”数据;
aggregate(聚合)返回单个聚合值,但transform不会减少数据量。


分组transform 操作
import pandas as pd

gapminder = pd.read_csv('gapminder.tsv',sep='\t')

# 需求: 按年分组,并计算组内每个人的预期寿命和该组平均年龄的差值
def lifeExp_diff(x):
    return x - x.mean()

a1 = gapminder.groupby('year').lifeExp.transform(lifeExp_diff)
print(a1)   # 注意,Length: 1704  数据的条数并没有减少



分组transform 填充缺失值
# 分组transform 填充缺失值
tips_10 = pd.read_csv('tips.csv').sample(10,random_state=42)    # 从中随机取出10条数据,random_state=42 固定随机数种子
# print(tips_10)
tips_10.iloc[[1,3,5,7],0] = np.nan  # 构建缺失值
# print(tips_10)
x = tips_10.groupby('sex').count()
# print(x)

# 定义函数,按性别分组填充缺失值
def fill_na_mean(x):
    # 计算平均值
    avg = x.mean()
    # 用平均值填充缺失值
    return x.fillna(avg)
    # return x.ffill()   # 前项填充

total_bill_group_mean = tips_10.groupby('sex').total_bill.transform(fill_na_mean)
print(total_bill_group_mean)
# 将计算的结果复制新列
tips_10['fill_total_bill'] = total_bill_group_mean
print(tips_10)



分组transform练习:减肥比赛
import pandas as pd

# 加载数据集,包含Bob、Amy两人从1月到4月每周的减肥记录
weight_loss = pd.read_csv('weight_loss.csv')

# 定义函数计算每人每周减肥比例
def find_perc_loss(s):
    return abs((s-s.iloc[0]) / s.iloc[0])   # s.iloc[0] 每月第一周的体重

# 查找Bob 1月份的数据
bob_jan = weight_loss.query('Name == "Bob" and Month == "Jan"')
# 测试计算减肥比的方法
find_perc_loss(bob_jan['Weight'])

# 计算每日每周的减肥比例
pcnt_loss = weight_loss.groupby(['Name','Month']).Weight.transform(find_perc_loss)
print(pcnt_loss)

# 增加减重比例这一列
weight_loss['Perc Weight Loss'] = pcnt_loss
print(weight_loss)

# 筛选出第四周的数据
week4 = weight_loss.query('Week == "Week 4"')
# 筛选bob的数据
week4_bob = week4.query('Name == "Bob"')[['Month','Perc Weight Loss']]
# 筛选Amy的数据
week4_amy = week4.query('Name == "Amy"')[['Month','Perc Weight Loss']]

# 比较减肥结果
x = week4_bob.set_index('Month') - week4_amy.set_index('Month')     # 将Month设置为行标签
print(x)



分组过滤

使用groupby方法还可以过滤数据,调用filter方法,传入一个返回布尔值的函数,返回False的数据会被过滤掉。

import pandas as pd

tips = pd.read_csv('tips.csv')
# print(tips)

# 统计不同用餐人数的数量
size_count = tips['size'].value_counts()
# print(size_count)

# 需求:过滤用餐人数数据不足30条的数据
tips_filter = tips.groupby('size').filter(lambda x : x['size'].count() > 30)
print(tips_filter['size'].value_counts())



分组对象

分组对象的基本操作
import pandas as pd

tips10 = pd.read_csv('tips.csv').sample(10,random_state=42)

# 调用groupby,创建分组对象
sex_group = tips10.groupby('sex')
print(sex_group)

在这里插入图片描述
注意:sex_group 是一个DataFrameGroupBy对象,如果想查看计算过的分组,可以借助groups属性实现。

print(sex_group.groups)

在这里插入图片描述
可以取出一组的全部数据

print(sex_group.get_group('Female'))

在这里插入图片描述

分组对象的遍历

通过DataFrameGroupBy对象,可以遍历所有分组,相比于groupby之后使用aggregate、transform和filter,有时候使用for循环解决问题跟简单。

for sex_group in sex_groups:
    print(type(sex_group))
    print(sex_group)

在这里插入图片描述
注意:DataFrameGroupBy对象不支持下标索引


分组对象的多个分组
# 分组对象的多个分组
# 按性别和用餐时间分别计算小费数据的平均值
group_avg = tips10.groupby(['sex','time'],as_index=False)   # 分组的列不默认为行标签
result = group_avg[['total_bill','tip','size']].mean()
print(result)

# 不指定也可以重新指定行标签  result.reset_index

在这里插入图片描述

数据透视表

数据透视表(Pivot Table)是一种交互式的表,可以进行某些计算,如求和与计数等。所进行的计算与数据跟数据透视表中的排列有关。
数据透视表的本质其实就是分组聚合
在这里插入图片描述

import pandas as pd

store_info = pd.read_excel('门店信息表.XLSX')
# print(store_info)

# 需求:获取各地区不同恩济店铺的数量
a = store_info.groupby(['地区编码','店铺等级'])[['店铺代码']].count()   # [['店铺代码']]结果为DataFrame,['店铺代码']结果为Series
print(a)

在这里插入图片描述

b = a.unstack() # 转化为另一格式输出
print(b)

在这里插入图片描述
下面我们用数据透视表来完成这个:

# 数据透视表
c = store_info.pivot_table(index='地区编码',columns='店铺等级',
                           values='店铺代码',aggfunc='count')   # values是指行列数据来源,aggfunc是指用什么方法得到的数据
print(c)

在这里插入图片描述
与分组聚合得出的结果是一致的


datetime数据类型

Python 中的datetime 对象

Python内置了datetime对象

from datetime import datetime
# 获取当前时间
t1 = datetime.now()
print(t1)


# 手动创建
t2 = datetime(2020,1,1,0,0)
print(t2)


# 两个日期可以相减
diff = t1 - t2
print(diff)     # 1766 days, 15:36:40.382824
print(type(diff))   # <class 'datetime.timedelta'>



Pandas 中的datetime 对象

pandas可以使用to_datetime 函数把数据转换成datetime类型。

# pandas 中的datetime类型
import pandas as pd
ebola = pd.read_csv('country_timeseries.csv')
ebola1 = ebola.iloc[:5,:5]
print(ebola1.info())

注意:date是日期对象,但显示的是object类型
在这里插入图片描述

# 转换为日期对象
ebola1['Date_dt'] = pd.to_datetime(ebola1['Date'])
print(ebola1.info())

在这里插入图片描述
我们也可以在加载数据时直接转化

# 在加载数据时直接转化
ebola2 = pd.read_csv('country_timeseries.csv',parse_dates=['Date'])
print(ebola2.info())

在这里插入图片描述


提取datetime的各个部分

# 提取datetime的各个部分
dt = pd.to_datetime('2021-06-01')
print(type(dt))     # <class 'pandas._libs.tslibs.timestamps.Timestamp'>
print(dt.year)
print(dt.month)

# 在数据集中提取,并保存在新的一列
ebola2['year'] = ebola2['Date'].dt.year
print(ebola2['year'])



日期运算和Timedelta

Ebola数据集中的Day列表示一个国家爆发Ebola疫情的天数。这一列数据可以通过日期运算重建该列。

# 日期运算和Timedelta
first_day = ebola2['Date'].min()    # 获取疫情爆发的第一天
# 计算疫情爆发的天数
ebola2['outbreak_day'] = ebola2['Date'] - first_day
ebola2_outbreak = ebola2[['Date','Day','outbreak_day']]
print(ebola2_outbreak)

在这里插入图片描述

TimedeltaIndex 对象

import pandas as pd

# 加载股票数据
tesla = pd.read_csv('TSLA.csv',parse_dates=['Date'])
# print(tesla.info())

tesla['Ref Date'] = tesla['Date'] - tesla['Date'].min()
# print(tesla['Ref Date'])

# 将Ref Date设置为行标签索引
tesla.index = tesla['Ref Date']
# print(tesla)

print(tesla['0 days' : '5 days'])

在这里插入图片描述

Date range 方法

head_range = pd.date_range(start='2014-12-31',end='2015-01-05')
print(head_range)

在这里插入图片描述

ebola2.index = ebola2['Date']
print(ebola2.head())

在这里插入图片描述
可以看到原始的Date不是连续排列的。

ebola2 = ebola2.reindex(head_range)
print(ebola2.head())

在这里插入图片描述
使用date range 方法就可以连续,缺失的数据由NaT,NaN表示。

注意: 使用date_range函数创建日期序列时,可以传入一个参数freq,默认为D,表示日期范围内的值是逐日递增的。
在这里插入图片描述



案例:银行数据分析

import pandas as pd

banks = pd.read_csv('banklist.csv')
print(banks.info())

在这里插入图片描述

banks['Close_year'] = banks['Closing Date'].dt.year
banks['Close_quarter'] = banks['Closing Date'].dt.quarter   # 季度
print(banks.T)

在这里插入图片描述

# 需求:统计每年倒闭的银行的数量
num_year_outbreak = banks.groupby('Close_year')['Bank Name'].size() # size计数包含NaN,count不包含NaN
num_year_outbreak.plot(figsize=(16,8))
# 以折线图显示
import matplotlib.pyplot as plt
plt.show()

在这里插入图片描述

# 需求:统计每年每季度的银行倒闭的数量
num_quater_outbreak = banks.groupby(['Close_year','Close_quarter'])['Bank Name'].size() # size计数包含NaN,count不包含NaN
print(num_quater_outbreak)
num_quater_outbreak.plot()
# 以折线图显示
plt.show()

在这里插入图片描述

案例:tesla股票数据分析

import pandas as pd

# 加载股票数据
tesla = pd.read_csv('TSLA.csv',parse_dates=['Date'])
print(tesla.info())

在这里插入图片描述

# 获取2015年8月的股票数据
tesla_2015_8 = tesla.loc[(tesla['Date'].dt.year == 2015) & (tesla['Date'].dt.month == 8)]
print(tesla_2015_8)

在这里插入图片描述

# 把Date变成行标签
tesla.set_index('Date',inplace=True)
# print(tesla.head())
# 把索引设置为日期对象后,可以直接使用日期来获取某些数据
# 示例:获取2016年的股票数据
print(tesla['2016-01-01':'2016-12-31'])



案例:丹佛报警记录数据分析

import pandas as pd

crime = pd.read_csv('crime.csv',parse_dates=['REPORTED_DATE'])
print(crime.info())

在这里插入图片描述

# 设置报警时间为行标签索引
crime = crime.set_index('REPORTED_DATE')
print(crime)

在这里插入图片描述

# 获取 2016-05-02 的报警记录数据
print(crime.loc['2016-05-02'])

# 获取2015-03-01 到 2015-06-01 之间的报警记录数据
# 排序索引并去除重复值(如果有)
crime = crime.sort_index().drop_duplicates()	# 不然会报错
print(crime.loc['2015-03-01' : '2015-06-01'])

# 索引也可以包含时分秒
print(crime.loc['2015-03-01 22' : '2015-06-01 20:35:00'])

# 查询凌晨两点到五点的报警记录
print(crime.between_time('2:00','5:00'))

# 查询在5:47分的报警记录
print(crime.at_time('5:47'))

注意:排序后再索引效率会变高,这串代码很重要

# 排序索引并去除重复值(如果有)
crime = crime.sort_index().drop_duplicates()
# 计算每周的报警数量
# 为了统计每周的报警数量,需要按周分组。resample重采样,可以按照指定时间周期分组。
weekly_crimes = crime.resample('W').size()
print(weekly_crimes)
# 也可以把周四作为每周的结束
weekly_crimes = crime.resample('W-THU').size()
print(weekly_crimes)

在这里插入图片描述
resample要求行索引必须是日期类型

# pandas绘图
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = 'SimHei'
weekly_crimes.plot(figsize=(16,8),title='丹佛报警记录情况')
plt.show()

在这里插入图片描述

# 分析每季度的犯罪和交通事故数据
crime_quarterly = crime.resample('Q')['IS_CRIME','IS_TRAFFIC'].sum()
print(crime_quarterly)

在这里插入图片描述
所有日期都是该季度的最后一天,也可以用QS改为该季度的第一天

crime_quarterly = crime.resample('QS')['IS_CRIME','IS_TRAFFIC'].sum()
print(crime_quarterly)

在这里插入图片描述

# 分析每周每一天的报警记录情况:可以通过Timestamp的dt属性得到周几,然后统计
crime = crime = pd.read_csv('crime.csv',parse_dates=['REPORTED_DATE'])  # 不能用行标签来进行索引,重新加载一下
wd_counts = crime['REPORTED_DATE'].dt.weekday.value_counts()
print(wd_counts)

在这里插入图片描述

# 分析每周每天的犯罪记录和交通事故情况
left = crime[crime['IS_TRAFFIC'] == 1]['REPORTED_DATE'].dt.weekday.value_counts()   # 按周统计交通事故情况
right = crime[crime['IS_CRIME'] == 1]['REPORTED_DATE'].dt.weekday.value_counts()    # 按周统计犯罪记录
result = pd.concat([left,right],axis=1) # 按列整合
result.columns = ['IS_TRAFFIC','IS_CRIME']  # 修改列名
print(result)

在这里插入图片描述



数据处理案例实操-零售会员数据分析

"""
业务背景:
1)某女鞋连锁零售企业,当前业务以线下门店为主,线上销售为辅
2)通过对会员的注册数据以及消费数据的分析,监控会员营运情况,为后续会员运营提供决策依据
3)会员等级说明:
    - 白银:注册(0)
    - 黄金:下单(1-3888)
    - 铂金:3888-6888
    - 钻石:6888以上

数据分析要达到的目标:
1)描述性数据分析
2)使用业务数据,分析出会员的营运的基本情况

分析会员运营的基本情况:
1)从量的角度分析会员营运情况
    - 整体会员营运情况(存量、增量)
    - 不同渠道(线上、线下)的会员运营情况
    - 线下业务,拆解到不同地区、门店会员运营情况
2)从质的角度分析会员营运情况
    - 会销比
    - 连带率
    - 复购率
"""
import pandas as pd
from datetime import datetime
import os
import matplotlib.pyplot as ply

每次读取文件都需要很长时间,调用os包保存为pkl文件,提升读取速度。
注意:KPL是Excel的一次映射 映射完之后是独立的 修改KPL只会影响KPL 等你做完操作之后需要修改后的excel 重新导出。

# 定义文件路径
excel_file = r'E:\\Data\\Python\\learn_py\\anaconda环境\\Python数据分析基础\\3、pandas数据处理\\会员信息查询.xlsx'
pickle_file = r'E:\\Data\\Python\\learn_py\\anaconda环境\\Python数据分析基础\\3、pandas数据处理\member_data.pkl'


# 检查是否已有pickle文件
if os.path.exists(pickle_file):
    # 如果 文件存在,直接加载
    member_df = pd.read_pickle(pickle_file)
else:
    # 如果 pickle 文件不存在,加载 Excel 文件并进行处理
    member_df = pd.read_excel(excel_file)
    # 将处理好的数据保存
    member_df.to_pickle(pickle_file)



第一部分:会员存量、增量分析

# 查看文件信息
print(member_df.info())

在这里插入图片描述

# 需要按月统计注册的会员数量,注册时间原始数据需要处理成年-月的形式
member_df['注册年月'] = member_df['注册时间'].apply(lambda x: x.strftime('%Y-%m') if pd.notnull(x) else None)   # 格式化,只要年月

在这里插入图片描述

# 按照注册年月来统计每月会员的增量
# 分组聚合
month_count = member_df.groupby('注册年月')[['会员卡号']].count()   # 返回的结果是DataFrame,如果要返回的是Series,month_count = member_df.groupby('注册年月')['会员卡号'].count()
month_count.columns = ['月增量']   # 修改列标签,只有dataframe才能修改列标签
print(month_count)

在这里插入图片描述

# 也可以用透视表来操作
month_count_t = member_df.pivot_table(index='注册年月',values='会员卡号',aggfunc='count')
print(month_count_t)

在这里插入图片描述

# 统计每个月的存量
month_count.loc[:,'存量'] = month_count['月增量'].cumsum()    # 数据的累加
print(month_count)

在这里插入图片描述

# 可视化
month_count2 = month_count[1:]  # 因为八月数据是之前的累加,所以先剔除
plt.rcParams['font.sans-serif'] = 'SimHei'  # 修改成中文
month_count2['月增量'].plot(figsize=(20,8),color='red',secondary_y=True)
month_count2['存量'].plot.bar(figsize=(20,8),color='gray',xlabel='年月',ylabel='存量')
plt.title('会员存量分析',fontsize=20)
plt.show()

在这里插入图片描述


第二部分:会员增量等级分布分析

# 第二部分:会员增量等级分布分析
# 按注册的年月,统计不同的会员等级的会员增量

# 分组聚合
month_degree_count = member_df.groupby(['注册年月','会员等级'])['会员卡号'].count().unstack()  # 变成列索引
print(month_degree_count)

在这里插入图片描述

# 透视表操作
month_degree_count_t = member_df.pivot_table(index='注册年月',columns='会员等级',values='会员卡号',aggfunc='count')
print(month_degree_count_t)

结果同上

# 可视化
month_degree_count2 = month_degree_count[1:]    # 剔除第一行数据
plt.rcParams['font.sans-serif'] = 'SimHei'  # 修改成中文
fig,ax1 = plt.subplots(figsize=(20,8),dpi=100)  # 坐标系,dpi:值域
ax2 = ax1.twinx()   # 复制一个坐标轴
month_degree_count2[['白银会员','黄金会员']].plot.bar(ax=ax1,rot=0,xlabel='年月',ylabel='白银黄金')
month_degree_count2[['铂金会员','钻石会员']].plot(ax=ax2,color=['red','gray'],ylabel='铂金钻石')
plt.title('会员增量等级分布')
plt.show()

在这里插入图片描述


第三部分:会员整体等级分布分析

# 第三部分:会员整体等级分布分析
# 计算各个等级的会员占总体的比列
# 分组聚合
ratio = member_df.groupby('会员等级')[['会员卡号']].count()
print(ratio)

在这里插入图片描述

# 透视表
ratio_t = member_df.pivot_table(index='会员等级',values='会员卡号',aggfunc='count')
print(ratio_t)

结果同上

# 修改列标签
ratio.columns = ['会员数'] 
print(ratio)

在这里插入图片描述

# 计算不同等级的会员占比
ratio.loc[:,'占比'] = ratio['会员数'] / ratio['会员数'].sum()
print(ratio)

在这里插入图片描述

# 可视化
plt.rcParams['font.sans-serif'] = 'SimHei'  # 修改成中文
ratio.loc[['白银会员','钻石会员','黄金会员','铂金会员'],'占比'].plot.pie(figsize=(20,8),autopct='%.1f%%',fontsize=16) # autopct百分比,fontsize字体大小
plt.show()

在这里插入图片描述


第四部分:线上线下增量分析

# 第四部分:线上线下增量分析
# 按照注册年月和会员来源统计会员增量
# 分组聚合
from_data = member_df.groupby(['注册年月','会员来源'])['会员卡号'].count().unstack()
print(from_data)

在这里插入图片描述

# 透视表
from_data_t = member_df.pivot_table(index='注册年月',columns='会员来源',values='会员卡号',aggfunc='count')
print(from_data_t)

结果同上

# 可视化
from_data2 = from_data[1:]  # 剔除
plt.rcParams['font.sans-serif'] = 'SimHei'  # 修改成中文
from_data2.plot(figsize=(20,8),fontsize=16,grid=True) # grid网格
plt.title('线上线下增量分析',fontsize=20)
plt.show()

在这里插入图片描述

第五部分:地区店均会员分析

# 第五部分:地区店均会员分析
# 加载数据
store_info = pd.read_excel('门店信息表.XLSX')
print(store_info.info())

在这里插入图片描述

# 获取需要的数据
area = store_info[['店铺代码','地区编码']]

# 将会员信息数和门店数据进行关联
country_info = member_df.merge(area,left_on='所属店铺编码',right_on='店铺代码')
print(country_info.head())

在这里插入图片描述

# 按照地区统计该地区下的会员数量
country_count = country_info[country_info['地区编码'] != 'GBL6D01'].groupby('地区编码')[['会员卡号']].count()
country_count.columns = ['会员数']
print(country_count)

在这里插入图片描述

# 统计每个地区下的店铺数量
country_count.loc[:,'店铺数'] = country_info[['地区编码','店铺代码']].drop_duplicates().groupby('地区编码')['店铺代码'].count()  # 去重再计数
print(country_count)

在这里插入图片描述

# 计算地区下面的平均会员数
country_count.loc[:,'地区平均会员数'] = country_count['会员数'] / country_count['店铺数']
print(country_count)

在这里插入图片描述

# 计算总平均会员数
country_count.loc[:,'总平均会员数'] = country_count['会员数'].sum() / country_count['店铺数'].sum()

# 排序
country_count = country_count.sort_values('地区平均会员数',ascending=False)
print(country_count)

在这里插入图片描述

# 可视化
plt.rcParams['font.sans-serif'] = 'SimHei'  # 修改成中文
country_count['地区平均会员数'].plot.bar(figsize=(20,8),color='r',legend=True,grid=True)
country_count['总平均会员数'].plot(figsize=(20,8),color='g',legend=True,grid=True)
plt.title('地区店均会员分析',fontsize=18)
plt.show()

在这里插入图片描述


第六部分:各地区会销比

# 第六部分:各地区会销比
# 加载数据
all_orders = pd.read_excel('全国销售订单数量表.xlsx')
print(all_orders.info())

在这里插入图片描述

# 按照地区和年月统计会员订单数
member_orders = all_orders.pivot_table(index='地区代码',
                                       columns='年月',
                                       values='会员订单数',
                                       aggfunc='sum',
                                       margins='all')   # 计算每行每列的和
print(member_orders)

在这里插入图片描述

# 按照地区和年月统计全部订单数
countey_sales =  all_orders.pivot_table(index='地区代码',
                                       columns='年月',
                                       values='全部订单数',
                                       aggfunc='sum',
                                       margins='all')   # 计算每行每列的和
print(countey_sales)

在这里插入图片描述

# 计算会销比
conference_sales_ratio = member_orders / countey_sales
conference_sales_ratio = conference_sales_ratio.applymap(lambda x: format(x,'.2%'))  # 变成百分比格式
print(conference_sales_ratio)

在这里插入图片描述

第七部分:各地区会员连带率分析

# 第七部分:各地区会员连带率分析
member_consume = pd.read_excel('会员消费报表.xlsx')
print(member_consume.info())

在这里插入图片描述

# 添加·年月·列
member_consume.loc[:,'年月'] = member_consume['订单日期'].apply(lambda x: datetime.strftime(x,'%Y%m'))
print(member_consume.head())

在这里插入图片描述

# 将会员消费数据和门店信息进行关联,获取地区信息
ret = member_consume.merge(store_info[['店铺代码','地区编码']],on='店铺代码')
# 取出电商渠道的数据(GBL6D01)
ret = ret[ret['地区编码'] != 'GBL6D01']
print(ret)

在这里插入图片描述

# 按照地区和年月统计会员的消费数量
consume_count = ret.pivot_table(index='地区编码',
                                columns='年月',
                                values='消费数量',
                                aggfunc='sum')

# 按照地区和年月统计会员的订单数
order_count = ret.pivot_table(index='地区编码',
                                columns='年月',
                                values='订单号',
                                aggfunc='nunique')  # nunique 去重计数

# 计算连带率
joint_liability_rate = consume_count / order_count
print(joint_liability_rate)

在这里插入图片描述

第八部分:各地区会员复购率分析

# 第八部分:各地区会员复购率分析

# 将会员消费数据和地区门店数据进行关联
order_data = member_consume[['年月','订单日期','卡号','订单号','订单类型','店铺代码']].merge(
    store_info[['店铺代码','地区编码']],
    on='店铺代码'
).query('订单类型 == "下单"') # 剔除退单的
print(order_data)

在这里插入图片描述

# 统计会员是否复购
order_data = order_data[['年月','订单日期','卡号','地区编码']].drop_duplicates()    # 去重,同一天复购多次也只算一次
print(order_data)

在这里插入图片描述

# 按地区和卡号统计会员的消费次数
consume_counte2 = order_data.pivot_table(index=['地区编码','卡号'],
                                       values='年月',
                                       aggfunc='count').reset_index()   # 重置索引
consume_counte2.rename(columns={'年月': '消费次数'},inplace=True) # 修改列名
print(consume_counte2)

在这里插入图片描述

# 判断会员是否复购
consume_counte2.loc[:,'是否复购'] = consume_counte2['消费次数'] > 1
print(consume_counte2)

在这里插入图片描述

# 统计该地区的消费人数和复购人数
result2 = consume_counte2.pivot_table(index='地区编码',
                            values=['消费次数','是否复购'],
                            aggfunc={'消费次数':'count','是否复购':'sum'}
                            )
result2.columns = ['复购人数','消费人数']
print(result2)

在这里插入图片描述

# 计算复购率
repurchase_rate = result2['复购人数'] / result2['消费人数']
repurchase_rate = repurchase_rate.to_frame(name='复购率')  # 转换为 DataFrame 并设置列名
repurchase_rate = repurchase_rate.applymap(lambda x: format(x,'.2%'))
print(repurchase_rate)

在这里插入图片描述




Python数据可视化

数据可视化常用库:

  1. Matplotlib(功能强大,代码相对复杂)
    • Matplotlib是Python编程语言的开源绘图库。它是Python可视化软件包中最突出的,使用最广泛的绘图工具。
    • Matplotlib在执行各种任务方面非常高效。可以将可视化文件导出为所有常见格式(PDF,SVG,JPG,PNG,BMP和GIF)。
    • Matplotlib可以创建流行的可视化类型-折线图,散点图,直方图,条形图,误差图,饼图,箱形图以及更多其他类型的图,还支持3D绘图。
    • 许多Python库都是基于Matplotlib构建的,Pandas和Seaborn是在Matplotlib上构建的
    • Matplotlib项目由John Hunter于2002年启动。Matplotlib最初是在神经生物学的博士后研究期间开始可视化癫痫患者的脑电图(ECoG)数据。
  2. Pandas (使用简单,功能稍弱)
    • Pandas的绘图功能基于Matplotlib,是对Matplotlib的二次封装
    • Matplotlib绘图时,代码相对复杂,使用Pandas绘制基本图表相对比较简单,更加方便
    • Pandas中常用的数据结构 series 和 dataframe 都有plot()方法,用于绘图
  3. Seaborn (推荐使用)
    • Seaborn是基于Matplotlib的图形可视化python开源库
    • Seaborn是在Matplotlib的基础上进行了更高级的API封装,从而使得作图更加容易
    • Seaborn的API设计偏向探索和理解数据
  4. echarts 和 pyecharts (追求可视化效果,推荐使用)
    • ECharts,是百度开源,使用 JavaScript 实现的开源可视化库,可以流畅的运行在 PC 和移动设备上,兼容当前绝大部分浏览器(IE8/9/10/11,Chrome,Firefox,Safari等),底层依赖矢量图形库 ZRender,提供直观,交互丰富,可高度个性化定制的数据可视化图表
    • pyecharts 是一个用Python生成 Echarts 图表的类库。



Matplotlib绘图

Matplotlib绘图入门

使用 Matplotlib 绘图,首先需要导入 pyplot 模块,该模块包含一系列绘图函数的相关函数

from matplotlib import pyplot as plt

Matplotlib提供了两种方法来作图:状态接口和面向对象:

  1. 状态接口
from matplotlib import pyplot as plt
# 准备数据
x = [-3, 5, 7]
y = [10, 2, 5]

# 创建画布
plt.figure(figsize=(15, 3))
# plot绘图
plt.plot(x, y)
# 设置x轴和y轴的取值范围
plt.ylim(0, 10)
plt.xlim(-3, 8)
# 设置x轴和y轴的标签
plt.xlabel('X Axis', size=20)
plt.ylabel('Y Axis')
# 设置标题
plt.title('Line Plot', size=30)

plt.show()

在这里插入图片描述


2) 状态接口

# 准备数据
x = [-3, 5, 7]
y = [10, 2, 5]

# 创建坐标轴对象
fig, ax = plt.subplots(figsize=(15, 3))
# 进行绘图
ax.plot(x, y)
# 设置x轴和y轴的范围
ax.set_xlim(-3, 8)
ax.set_ylim(0, 10)
ax.set_xlabel('X Axis', size=12)
ax.set_ylabel('Y Axis', size=12)
ax.set_title('Line Plot', size=20)

plt.show()

在这里插入图片描述


matplotlib 数据可视化案例

本案例通过 seaborn 模块中的 Anscombe 数据集说明数据可视化的重要性

  • Anscombe 数据集由英国统计学家Frank Anscombe创建,数据集包含4组数据,每组数据包含两个连续变量
  • 每组数据的平均值、方差、相关性都相同,但是当它们可视化后,就会发现每组数据的模式明显不同
# 加载seaborn 模块中提供的 anscombe 数据集
import seaborn as sns
anscombe = sns.load_dataset('anscombe')

# 加载数据中的 dataset 列,用来区分整个数据集中的子数据集
dataset_1 = anscombe[anscombe['dataset']=='I']
dataset_2 = anscombe[anscombe['dataset']=='II']
dataset_3 = anscombe[anscombe['dataset']=='III']
dataset_4 = anscombe[anscombe['dataset']=='IV']

# 查看数据的统计分布情况
print(dataset_1.describe())
print(dataset_2.describe())
print(dataset_3.describe())
print(dataset_4.describe())

在这里插入图片描述


从数据的统计量看,变量X,Y,4个子数据集的平均值和标准差基本相同,但是平均值和标准差相同,几个数据集就完全相同么?下面绘制图形来查看一下这 4 个子数据集。

# 在创建的各个坐标轴中绘制图表
axes1.plot(dataset_1['x'], dataset_1['y'], 'o')
axes2.plot(dataset_2['x'], dataset_2['y'], 'o')
axes3.plot(dataset_3['x'], dataset_3['y'], 'o')
axes4.plot(dataset_4['x'], dataset_4['y'], 'o')

# 调整子图之间的间距
fig.tight_layout()

# 显示图像
plt.show()

在这里插入图片描述

# 为每个子图添加标题
axes1.set_title('dataset_1')
axes2.set_title('dataset_2')
axes3.set_title('dataset_3')
axes4.set_title('dataset_4')

# 为大图添加标题
fig.suptitle('Anscombe Data')


# 调整子图之间的间距
fig.tight_layout()

# 显示图像
plt.show()

在这里插入图片描述


使用 Matplotlib 绘制统计图

import seaborn as sns
# 使用 seaborn 库的 tips 数据集,其中包含了某餐厅服务员收集的顾客付小费的相关数据
tips = sns.load_dataset('tips')
print(tips.head())

在这里插入图片描述


单变量绘图(直方图)

在统计学属于中,单变量(univariate)指单个变量。

  • 直方图是观察单个变量最常用的方法。这些值是经过"装箱"(bin)处理的
  • 直方图会将数据分组后绘制成图来显示变量的分布状况
from matplotlib import pyplot as plt
fig = plt.figure()
axes1 = fig.add_subplot(1, 1, 1)
# 绘制直方图
axes1.hist(tips['total_bill'], bins=10)

# 设置 x轴标题、y轴标题、坐标系的标题
axes1.set_xlabel('Total Bill')
axes1.set_ylabel('Frequency')
axes1.set_title('Histogram of Total Bill')

# 调整子图之间的间距
fig.tight_layout()

# 显示图像
plt.show()

在这里插入图片描述


双变量绘图(散点图)

双变量(bivariate)指两个变量

  • 散点图用于表示一个连续变量随另一个连续变量的变化所呈现的大致趋势
scatter_plot = plt.figure()
axes1 = scatter_plot.add_subplot(1, 1, 1)
# 绘制散点图
axes1.scatter(tips['total_bill'], tips['tip'])

# 设置 x轴标题、y轴标题、坐标系的标题
axes1.set_xlabel('Total Bill')
axes1.set_ylabel('Tip')
axes1.set_title('Scatterplot of Total Bill vs Tip')


# 调整子图之间的间距
fig.tight_layout()

# 显示图像
plt.show()

在这里插入图片描述


多变量绘图(二维平面)
  • 二维平面可以用来展示两个变量的数据,如果是多变量,比如添加一个性别变量,可以通过不同的颜色来表示
  • 还可以通过圆点的大小来区分变量的不同,但如果变量的大小区别不大,可能通过圆点大小来区分效果不是很好
# 多变量绘图(二维平面)
def recode_sex(sex):
    if sex == 'Female':
        return 0
    else:
        return 1

tips['sex_color'] = tips['sex'].apply(recode_sex)

scatter_plot = plt.figure()
axes1 = scatter_plot.add_subplot(1,1,1)
# 绘制散点图
scatter = axes1.scatter(x=tips['total_bill'], y=tips['tip'],
              s=tips['size']*10, c=tips['sex_color'], alpha=0.5)

# 设置 x轴标题、y轴标题、坐标系的标题
axes1.set_xlabel('Total Bill')
axes1.set_ylabel('Tip')
axes1.set_title('Total Bill vs Tip Colored by Sex and Sized by Size')

# 添加图例
legend = axes1.legend(*scatter.legend_elements(), title="gender")
axes1.add_artist(legend)


# 调整子图之间的间距
fig.tight_layout()

# 显示图像
plt.show()

在这里插入图片描述


pandas绘图

pandas 数据可视化简介

pandas库是Python数据分析的核心库:

  • 它不仅可以加载和转换数据,还可以做更多的事情:它还可以可视化
  • pandas 绘图 API 简单易用,是 pandas 流行的重要原因之一

pandas 单变量可视化

单变量可视化: 包括条形图、折线图、直方图、饼图等。

柱状图和分类数据

柱状图是最简单最常用的可视化图表

条形图(柱状图)非常灵活:

  • 高度可以代表任何东西,只要它是数字即可
  • 每个条形可以代表任何东西,只要它是一个类别即可。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值