数据分析 NO.12 pandas数据管理

pandas数据管理

1.文件读取
pandas提供了read_csv函数可以将文件按照固定的格式进行读取,函数能够自动解析数据类型,添加列明与索引等很多功能,能够以结构化的dataframe形式存储数据。

df=pd.read_csv("     ",sep="\t")

sep是以什么作为分隔符划分的(可以指定)。
index_col=0 #使第一列成为索引
sheet_name = None #一次性读取表中的所有Sheet,之后的df 是一个 df_ordict 需要用for提取

df_all = pd.read_excel(path + "\\" +file,sheet_name=None)
for df in df_all:
	df = df_all[df]

其余参数可以参考:链接

dataframe是二维结构化数据,series是一维数据。 dataframe有一个或者多个series组成,dataframe的一行或者一列就是一个series。

2.创建一个Series
下面代码中的Series的S 一定要大写!!

a={"name":"xiaoming","age":18,"sex":"male"}
s = pd.Series(data, index=index)

b = [1,2,3,4,5,6]
s1 = pd.Series(b,index = list("abcdef"))

如果索引个数多余了值,值会根据索引补充到相同。但是如果值有2个,索引有3个。那么会报错。如果一个值,而多个索引的话,会复制那一个值给每一个索引

3.创建一个DateFrame
字典转换DateFrame:
在这里字典插入图片描述
列表转换DateFrame:
在这里插入图片描述
Series转换DateFrame:
在这里插入图片描述
一般来说创建DataFrame都要自己指定列名!

4.常用操作:
查看属性:
df.columns 查看列名,但是返回的也是一个Index的类( list(df) 也可以直接获得列名并且是列表 )
df.index 返回索引
df.dtypes 返回每一列数据的类型是什么
df.shape 有多少行,多少列
df.size 有多少个元素
df.empty 判断是否数据库为空

常用方法:
df.head()
df.tail() 返回最后多少行
df.rename() df.rename(columns={“height”:“Height”,“weight”:“Weight”})
df里面对数据集的操作大多数产生一个新的数据集操作,不对原数据集进行修改。

axis = 0 针对列 Column
axis = 1 针对行 row

df.replace() df.replace({“Player”:{“abc”:“ABC”}}) 其中Player是告诉程序哪一列,后面的是将abc变成ABC
df[“name”] = df[“name”].replace(“小明”,“小王”) #把该列中小明都替换成小王
df[“name”] = df[“name”].replace([“小明”,“小红”],“小王”) #把该列中小明、小红都替换成小王

df.collage.value_counts() 对collage进行count
df.sort_values() df.sort_values(by=[“Height”,“collage”]) by后面是按照什么排序。
df.describe() 对数据框内的数值型变量进行统计 还是挺常用的
df.max()默认是对列起作用

5.数据选取/添加/删除
选择列数据:
df[‘Player’] 推荐这种方式
df[[‘Player’,‘height’]]
df.Player

增加一列:每次只能增加一列
df[“class”] = 1

条件筛选:
df[(df[‘height’] >= 200) | (df[‘height’] <=170)]

删除:对本身进行修改
del df[“class”]

主要是可以添加参数axis,进行行列分别计算。

import numpy as np 
a = np.array([[1,2,3,4,5,56],[3,4,5,1,7,3],[29,3,1,6,2,0]])
np.sum(a,axis = 1)
np.sum(a,axis = 0)
np.sum(a) # 全部求和

6.Missing value
pandas使用numpy.nan来代表缺失值。缺失值不会被程序计算。处理的方式:
1.删除含有缺失值的行

df.dropna() #默认的是axis=0,对行进行操作
df.dropna(thresh=12,axis=0) #thresh参数 保留至少有n个非Nan的行/列

2.填充缺失值

df.fillna()

3.检测缺失值:

df.isnull()
df["xx"].isnull()
df.isnull().any()

4.筛选空值和缺失值

# 筛选出A列为空的所有行
df[df["A"].isnull()]
# 筛选出A列非空的所有行
df[df["A"].notnull()]       
# 删除缺失值的行 不会修改原有数据框 默认axis=0按行,how=any每行存在一个空值就执行删除行操作
df.dropna(axis=0, how='any', thresh=None, subset=None, inplace=False)
subset根据某个子集进行删除,比如之前的'hight',“wegiht”,【'hight',“wegiht”】

df.dropna(how="all")  # 删除所有列都为空值的特定行
df.dropna(how = "any")   # 删除存在空值的行

# 填充缺失值
df.fillna(value=None, method=None, axis=None, inplace=False, limit=None, downcast=None, **kwargs)

# 用字典来填充
values = {'A': 0, 'B': 1}   # A列空值用0填充,B列空值用1填充
df.fillna(value=values)  

# 不同的填充方式{‘backfill’, ‘bfill’, ‘pad’, ‘ffill’, None}
# 每列的空值,用其列下方非空数值填充
df.fillna(method="backfill")  
df.fillna(method="bfill")   # 同backfill
# 每列的空值,用其所在列上方非空数值填充,若上方没有元素,保持空值
df.fillna(method="ffill")  
df.fillna(method="pad")     # 同 ffill
 
 
#limit参数设置填充空值的最大个数
df.fillna(0,limit=1)  # 每列最多填充1个空值,超过范围的空值依然为空
 
#inplace参数空值是否修改原数据df
df.fillna(0,inplace=True)  # inplace为true,将修改作用于原数据

7.文本数据

s = pd.Series(['A', 'B', 'C', 'Aaba ', ' Baca', 'CABA ', 'dog', 'cat'])
s.str.strip()   #获取S中的字符
s.str.upper()
s[s.str.strip().str.endswith("a")]

一个很常用的场景就是当你的index或者column名称前后包含了空格的时候,你可以用str的方法剔除这些空格,从而避免不必要的麻烦

a = {"name ":["xiaoming","xiaohong","xiaogang"]," age":[12,13,14]}
test = pd.DataFrame(data = a)
test['age'] # ERROR
test.columns = test.columns.str.strip()   比较常用

split方法用于根据某个分隔符对字符进行分割,返回一个列表

df['Player'].str.split(" ")
# 使用get方法获取指定位置的元素
df['Player'].str.split(" ").str.get(1)

以下如果后面加了参数分割变自动成为列

# 使用expand方法
df['Player'].str.split(" ",expand = True)     这里使用了expand 展示出来的效果会有框线

使用[]对字符串的位置进行索引选取

df['Player'].str[:3]

8.索引选取
.loc 基于索引名字的选择。

df.loc[0]
df.loc[0:5]  5包含进去的,所以不存在前闭后开
df.loc[df["weight"]>=180]

.iloc 基于索引位置的选择

df.iloc[0:5]   前闭后开   
df.iloc[[1,2,3]]
df.loc[:10,["player","wegiht"]]  逗号后面是对列的选择 
df.iloc[:10,[0,1]]   这回基于位置的,所以后面的列也必须是位置索引

数据过滤:

df.loc[(df['height']>=180) & (df['weight']>=80)]

df[(df['height']>=180) & (df['weight']>=80)]

上面两种方法都可以,似乎没体现出.loc有什么优势。那么我们换个提问:
创建一个新列,
如果height >= 180, weight >=80, 值为 “high"
如果height<= 180 并且 height >=170, weight<= 80 并且 weight >=70 值为 ”msize"
其余的值为 “small”

df.loc[(df['height'] >=180) & (df['weight'] >=80),"flag"] = "high"
df.loc[((df['height'] <=180) & (df['height']>=170)) &  ((df['weight'] <=80) & (df['weight'] >=70)),"flag"] = "msize"
df.loc[~(((df['height'] >=180) & (df['weight'] >=80)) |(((df['height'] <=180) & (df['height']>=170))&((df['weight'] <=80) & (df['weight'] >=70)))),"flag"] = "small"

此处上,代表else的意思就是取反 用 “~”就可以了!!

数据过滤还可以使用 .isin() 和 str.contains()

如果要选择某列等于多个数值或者字符串时,要用到.isin()(isin()括号里面应该是个list)

df[df["age"].isin([30,60,90])]

df1[df1["id"].isin(df2["id"])]#筛选df1的学生id同时也在df2中

平时使用最多的筛选应该是字符串的模糊筛选,在SQL语句里用的是like,在pandas里我们可以用.str.contains()来实现。

df[df["姓名"].str.contains("杨")]
df[df["姓名"].str.contains("杨|马")]

注意,这个‘|’是在引号内的,而不是将两个字符串分别引起来。’&‘在这里不能用。
有的时候在筛选的时候会报错有空值(Na/Nan)的情况,需要先将该列空值替换。如若仍然报错则是因为该列有的值不是str,需要提前将其转换。

df[“姓名”] = df[“姓名”].astype("str")

在这里插入图片描述

9.多重索引

new_df = df.set_index(keys=['birth_city','birth_state'])
new_df = df.set_index(keys=['birth_city','birth_state'],append=True,drop = False)  (append是否将原来的索引保存,drop是否丢掉原数据集里用作索引的这几个索引)
new_df.sort_index(na_position="last",inplace=True) #将索引进行排序,na_position 是指如果有缺失值就把他放在最后显示

要取出多行索引

new_df.loc[("abc","bcd")] 用元祖的形式放入两个索引

可以使用切片(slicers)对多重索引进行操作:
1)你可以使用任意的列表,元祖,布尔型作为Indexer
2)可以使用sclie(None)表达在某个level上选取全部的内容,不需要对全部的level进行指定,它们会被隐式的推导为slice(None)
3)所有的axis必须都被指定,意味着index和column上都要被显式的指明
4)我们应该对多重索引进行排序

new_df.loc[(slice(None),['Akron','Ahvaz','Albany'],slice(None)),:]  slice(1,1000)里面可以放一个范围
type(new_df.loc[(slice(None),['Akron','Ahvaz','Albany'],['Ohio','New York']),"Player"]) 其中先把3个索引用元祖括起来,然后分别选择,逗号后面是用作列
# IndexSlice是一种更接近自然语法的用法,可以替换slice
idx = pd.IndexSlice

new_df.loc[idx[:,:,['Ohio','New York']],:idx["Player"]]  #   :idx["Player"] 这里的冒号,前面没有的就是指范围到Player

new_df.loc[idx[0:500,['Brooklyn'],['Ohio','New York']],:idx["Player"]]

new_df.reset_index() 就是set_index的逆操作

df.rank()#排序特征 将其中一列排序.会有排名平均值
其中默认参数method=“average” ,也可以使用"first"
在这里插入图片描述

10.分组计算

grouped = df.groupby("director_name",as_index=False)  #as_index是指将分的组不作为索引

grouped.size() 有多少个组,每个组有多少条数据
grouped.groups()
把grouped一一取出来

for name,group in grouped:
    print(name)
    print(type(group))

grouped.get_group(name[, obj])
获得某一个分组的具体信息

In [2]: grouped.get_group("男")
Out[2]: 
 年龄 性别 成绩
0 15 男 优秀
2 15 男 及格
4 13 男 及格
6 15 男 优秀
7 16 男 差

可以先通过循环获得所有的组的名称

for name in grouped:
 print(name)# 获得所有分组的名称
 grouped.get_group(name) #获得所有该名称的数据

分组计算很重要的一点是:我们的每一个统计函数都是作用在每一个group上,不是单个样本,也不是全部数据

grouped.mean()
grouped.sum()
grouped.std()
# 只针对某个特征进行计算
grouped['duration'].sum()

# 使用agg函数进行多个统计量计算
import numpy as np 
grouped['duration'].agg([np.mean,np.sum,np.std])
#使用agg函数进行自己的聚合函数运算
def test(arr):
	return arr.max()-arr.min()
grouped.agg(test)

#不同列应用不同统计量
grouped.agg({"duration":np.mean,"director_facebook_likes":np.sum})

在这里插入图片描述
groupby apply + 自定义函数

df.groupby("date",as_index=False).apply(lambda x:x.sort_ values("money",ascending=True).iloc[:3])

获取分组后某一列最大值:

df.groupby("date",as_index=False)["col"].max()

获取分组后某一列最大值的索引:

idx = df.groupby("date",as_index=False)["col"].idxmax()
max_rows = df.loc[idx]

Transformation: 对数据做一些转换,比如标准化。都是对组内
Filteration:对group做过滤

df1=df.fillna(0)
grouped = df1.groupby("director_name")
z_score = lambda s : (s-s.mean())/ s.std()
grouped[['num_critic_for_reviews','duration','director_facebook_likes']].transform(z_score)

Filteration 就是过滤

grouped.filter(lambda s:len(s)>1)

grouped.filter(lambda g : g['duration'].mean() >= 150)

如果是数据非常多的时候(比如几千万条数据),运行的效率是比较低的,因为这个时候只使用了一个CPU线程,所以当数据非常多的时候,处理起来会很慢。这个时候CPU其他的核是空闲的,所以考虑使用joblib来多线程加速

from joblib import Parallel, delayed

def data_process(x):
	# process
	return ...
	
def applyParallel(dfGrouped, func):
    res = Parallel(n_jobs=4)(delayed(func)(group) for name, group in dfGrouped)
    return pd.concat(res)
    
result = applyParallel(df.groupby('user_id'), data_process)

#列子
res = Parallel(n_jobs=5)(delayed(data_parse_xiancheng)(name) for name in names)  #函数的参数在元祖内输入即可(name,group)

data_parse_xiancheng(name)#对应函数

11.表联结
提供了类似于SQL的join接口,供我们进行多表组合。不同的是,pandas可以对index进行join

一般来说是结构相同的联结

# 样本数据
df1 = pd.DataFrame({'A': ['A0', 'A1', 'A2', 'A3'],
                     'B': ['B0', 'B1', 'B2', 'B3'],
                     'C': ['C0', 'C1', 'C2', 'C3'],
                     'D': ['D0', 'D1', 'D2', 'D3']},
                     index=[0, 1, 2, 3])
 

df2 = pd.DataFrame({'A': ['A4', 'A5', 'A6', 'A7'],
                     'B': ['B4', 'B5', 'B6', 'B7'],
                     'C': ['C4', 'C5', 'C6', 'C7'],
                     'D': ['D4', 'D5', 'D6', 'D7']},
                      index=[4, 5, 6, 7])
 

df3 = pd.DataFrame({'A': ['A8', 'A9', 'A10', 'A11'],
                     'B': ['B8', 'B9', 'B10', 'B11'],
                     'C': ['C8', 'C9', 'C10', 'C11'],
                     'D': ['D8', 'D9', 'D10', 'D11']},
                     index=[8, 9, 10, 11])

result = pd.concat([df1,df2,df3])

Database-style DataFrame joining/merging:
merge函数用来对两张表进行join,非常类似于sql当中的表联结。 pandas里面不仅可以对columns进行Join,还可以对index进行join。

pd.merge(left, right, how=‘inner’, on=None, left_on=None, right_on=None,left_index=False, right_index=False, sort=True,
suffixes=(‘_x’, ‘_y’), copy=True, indicator=False, validate=None)
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

result = pd.merge(left, right, how='left', on=['key1', 'key2'])
result = pd.merge(left, right, how='right', on=['key1', 'key2'])
result = pd.merge(left, right, how='outer', on=['key1', 'key2'])

上面根据Key进行联结,要用on,并且左右两表的key要一样才可以!
下面是用索引进行索引,全部都要用,索引组合要用布尔值。

left.set_index(["key1","key2"],inplace=True)
right.set_index(["key1","key2"],inplace=True)
pd.merge(left,right,left_index=True,right_index=True)

下述如果key不同,则要采用下面代码形式,而且必须位置相同即(key1对key3,)

pd.merge(left,right,left_on = ['key1','key2'],right_on = ['key3','key4'])

索引和列名进行联结:
在这里插入图片描述
表的组合都是两张两张组合!

merge多表合并:用reduce+merge的方法

from functools import reduce
dfs = [df0, df1, df2, dfN]
df_final = reduce(lambda left,right: pd.merge(left,right,on=‘name’), dfs)

12.数据透视表:
pivot_table 提供了类似于EXCEL数据透视表的功能,重点的参数如下:

data: A DataFrame object
values: a column or a list of columns to aggregate
index: a column, Grouper, array which has the same length as data, or list of them. Keys to group by on the pivot table index. If an array is passed, it is being used as the same manner as column values.
columns: a column, Grouper, array which has the same length as data, or list of them. Keys to group by on the pivot table column. If an array is passed, it is being used as the same manner as column values.
aggfunc: function to use for aggregation, defaulting to numpy.mean
crosstab 用于计算两个以上的因子的cross-tabulation. 默认的是计算因子之间的频率,除非指定了其它数组或者函数进行计算

可参考:pivot_table详解

pd.crosstab(df['director_name'],df['color'],margins=True) #margins参数是汇总的意思

index: array-like, values to group by in the rows
columns: array-like, values to group by in the columns
values: array-like, optional, array of values to aggregate according to the factors
aggfunc: function, optional, If no values array is passed, computes a frequency table
rownames: sequence, default None, must match number of row arrays passed
colnames: sequence, default None, if passed, must match number of column arrays passed
margins: boolean, default False, Add row/column margins (subtotals)
normalize: boolean, {‘all’, ‘index’, ‘columns’}, or {0,1}, default False. Normalize by dividing all values by the sum of values.

pd.pivot_table(df,values=["duration"],index=["color"],columns=["director_name"])
pd.pivot_table(df,values = ['duration','director_facebook_likes'],columns = ['director_name'],index=['color'],aggfunc=[np.sum,np.mean])

13.对时间序列的处理
有两种方法 之前提到过的datetime.datetime.strptime()和pd.to_datetime()的方法。其中可以为了书写方便导入模块的时候可以写成
from datetime import datetime

df["datatime"]=df.apply(lambda s: datetime.datetime.strptime(s["datatime"],"%Y/%m/%d %M:%S"),axis=1)

df["datatime"] = pd.to_datetime(df["datatime"],format="%Y/%m/%d %M:%S")

其中to_datetime()当遇到一列时间序列中格式不匹配或者混有其他数据时,其本身的参数中errors可以出过滤筛选 errors=“ignore” #忽略 errors=coerce#去掉,to_datetime()本身也可以把空值,空字符串等转化为NaT
NaT(Not a Time)是时间戳数据里面的NA值(缺失值)

当时间序列位索引的时候,可以用切片的方式选择时间范围
搭配使用的还有 datetime.timedelta(),作用是使用时间间隔,里面参数可选择years,days,hours等

df[df["区间"]>datetime.timedelta(seconds=5)]

针对时间做了差分或者相减得出的值是timedelta格式,可以使用方法 total_seconds()将其转化为秒。

df["jiange"] = df["end_time"] - df["start_time"]
df["jiange"][0].total_seconds()

out: xxxxx s

diff() 对列或者行进行差分:
针对pandas:
df.diff() 后面一行对上一行做差值,如果是时间序列可以计算其时间间隔。
其中df.diff()里面可以输入参数,正数和默认是下面一行对上面一行做差,也可以填负数。

df["date"] = pd.to_datetime(df["date"],format="%Y/%m/%d %M:%S")
df["date"].diff()

在这里插入图片描述
针对在numpy中,使用np.diff()

numpy.diff(a, n=1,axis=-1)

a 表示需要进行差分操作的数据
n 代表执行几次差值
axis:默认是-1。
在这里插入图片描述

关于重采样resample可以参考pandas练习题文末
通过升采样如果没有的值会用NaN代替,也可以设置resample的参数用前面的值填充这些空白。

te.reasample("B",fill_method="ffill")

取时间范围可以把时间序列作为索引然后选择也可以使用pd.date_range()
默认情况下按天计算,如果只传入起始时间或结束时间,那就需要传入一个一段时间的参数

index = pd.date_range(start/end="2019/10/10",periods=20)#20天
index = pd.date_range("2019/1/1","2019/11/11",freq="BM")#选出符合频率的日期

默认保留起始和结束时间,也可以传入一些固有频率比如(“BM”)可参考resample里。
freq=参数也可以自己设置偏移量“H”,“4H”,“2h30min”.

在这里插入图片描述在这里插入图片描述
移动数据:沿着时间轴将数据前移或后移保持索引不变。shift()方法

df["datetime"].shift(2)#移动2行向下
df["datetime"].shift(2,freq="M")

python中时间日期格式化符号:
%y 两位数的年份表示(00-99)
%Y 四位数的年份表示(000-9999)
%m 月份(01-12)
%d 月内中的一天(0-31)
%H 24小时制小时数(0-23)
%I 12小时制小时数(01-12)
%M 分钟数(00-59)
%S 秒(00-59)
%a 本地简化星期名称
%A 本地完整星期名称
%b 本地简化的月份名称
%B 本地完整的月份名称
%c 本地相应的日期表示和时间表示
%j 年内的一天(001-366)
%p 本地A.M.或P.M.的等价符
%U 一年中的星期数(00-53)星期天为星期的开始
%w 星期(0-6),星期天为 0,星期一为 1,以此类推。
%W 一年中的星期数(00-53)星期一为星期的开始
%x 本地相应的日期表示
%X 本地相应的时间表示
%Z 当前时区的名称
%% %号本身

对于Series中选出独一无二的数据可以使用.unique()
对于DataFrame中重复列过滤可以使用.drop_duplicates(subset=[“a”,“b”])#删除A列和B列同时重复的值
(其中默认的是保存第一个出现的值组合,可以传入keep="last"则保留最后1个)

df[df["name"].duplicated()]#筛选出重复值

连续数据进行离散化可以使用.cut()

bins=[18,30,60,100]
df_age=pd.cut(df["age"],bins)
pd.value_counts(df_age)

其中可以对cut里进行参数设置
其中[ ],[ ),括号方圆的选择可以使用right=False

df_age=pd.cut(df["age"],bins,right=False)

自己设置label的名称labels=[,]

names=["young","middle","old"]
df_age=pd.cut(df["age"],bins,right=False,labels=names)

平均分成几个区间

df_age=pd.cut(df["age"],5)

把分类变量变化成为数值型变量(指标矩阵)
pd.get_dummies()

其中可以给处理后的数据的列名进行前缀添加可以使用prefix=“”

dummies=pd.get_dummies(df['data'],prefix="new")

pandas衍生库:pandas_profiling
其中可以一键生成数据相关报告

import pandas_profiling
data=pd.read_csv("")
file=data.profile_report(title="")
#转化成相应的Html文件
file.to_file(output_file="result.html")

对数据中百分位数进行计算,当q=0.25,0.5,0.75计算4分位数
df.quantile(q=0.5, axis=0, numeric_only=True)

常用函数总结:python pandas常用函数

求相关性:df.corrwith(df[“age”])
可参考corrwith用法
上述用法会自动计算一个df中所有列与参数的相关性
如果是某一列即一个Series可使用:

result = df["age"].corr(df["height"])
result_2 = df.corr() #自动计算各列之间的相关性

map和apply的区别:
apply 用在dataframe上,用于对row或者column进行计算;
map (其实是python自带的)用于series上,是元素级别的操作。
applymap 用于dataframe上,是元素级别的操作,是针对dataFrame里面的每一个元素的

df=pd.read_csv()
df["name"]=df["name"].apply(lambda s:s.upper())
df["name"]=df["name"].map(lambda s:s.upper())
df=df.applymap(lambda s:s.upper())

其中map可以有映射的使用

name={
"a":"beijing",
"b":"sichuan",
"c":"shanghai"
}
df["id"].map(name)#可以映射出对于字典值,新增一列比较实用!
#由于map直接作用于元素上,因此速度要比apply匿名函数速度快
df["Temp"] = df[2].apply(lambda x:func(x))
df["Temp"] = df.apply(lambda x:func(x[2]),axis=1)#速度和上一样
df["Temp"] = df[2].map(func) #速度最快

关于unstack()和stack()的理解可参考于:stack()和unstack()理解

pd.DataFrame(data.stack())#将原来的列转换为行索引

pd.DataFrame(data.unstack())#将原来的列索引转化了的最内层索引

pandas在算数运算过程中会自动将数据对齐。行列对应计算

把一列数据类型都转化成字符型

df["age"] = df["age"].astype(str)

关于pandas中dataframe和Series之间的加减:
如果是DataFrame和DataFrame,Series和Series之间,只要对齐,直接相加减即可。
如果是DataFrame与Series之间:
则需要使用df.sub()标记那一列相减。
axis=0表示希望匹配的轴是行,按照行来匹配,减去每列的数值

df["new"] = df.median(axis=1)
df.sub(df["new],axis=0)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值