python 数据分析----pandas

pandas是一个强大的Python数据分析的工具包。

pandas是基于NumPy构建的。

pandas的主要功能

  • 具备对其功能的数据结构DataFrame、Series
  • 集成时间序列功能
  • 提供丰富的数学运算和操作
  • 灵活处理缺失数据

安装方法:pip install pandas

引用方法:import pandas as pd

Series:

是带有标签的一维数组,可以保存任何数据类型(整数,字符串,浮点数,Python对象等)。轴标签统称为索引创建Series的基本方法是调用:

s = pd.Series(data, index=index)

data 可以是:

  • Python dict(字典)
  • ndarray
  • 数字

如果data是ndarray,则索引必须与数据长度相同。如果没有传递索引,将创建值为[0, ..., len(data) 1]的索引。

复制代码
In [125]: import pandas as pd

In [126]: a = pd.Series([4,7,-5,3],index=['a','b','c','d'])

In [127]: a
Out[127]: 
a    4
b    7
c   -5
d    3
dtype: int64


In [130]: b = pd.Series({'a':1,'b':2})

In [131]: b
Out[131]: 
a    1
b    2
dtype: int64
复制代码

获取值数组和索引数组:values属性和index属性

In [133]: a.values
Out[133]: array([ 4,  7, -5,  3])

In [134]: a.index
Out[134]: Index(['a', 'b', 'c', 'd'], dtype='object')

Series特性:

  • 从ndarray创建Series:Series(arr)
    复制代码
    In [140]: arr = np.array([1,2,3,4,5]) #必须是一维数组
    
    In [141]: a = pd.Series(arr)
    
    In [142]: a
    Out[142]: 
    0    1
    1    2
    2    3
    3    4
    4    5
    dtype: int64
    复制代码
  • 与标量运算:sr*2
    复制代码
    In [142]: a
    Out[142]: 
    0    1
    1    2
    2    3
    3    4
    4    5
    dtype: int64
    
    In [143]: a*2
    Out[143]: 
    0     2
    1     4
    2     6
    3     8
    4    10
    dtype: int64
    复制代码
  • 两个Series运算:sr1+sr2. 注意索引得一样,否则报NaN错误(not a number)
    复制代码
    In [145]: b
    Out[145]: 
    a    4
    b    7
    c   -5
    d    3
    e    0
    dtype: int64
    
    In [146]: a
    Out[146]: 
    0    1
    1    2
    2    3
    3    4
    4    5
    dtype: int64
    
    In [147]: b+a
    Out[147]: 
    a   NaN
    b   NaN
    c   NaN
    d   NaN
    e   NaN
    0   NaN
    1   NaN
    2   NaN
    3   NaN
    4   NaN
    dtype: float64
    
    
    In [150]: b = pd.Series(np.arange(5))
    
    In [151]: a
    Out[151]: 
    0    1
    1    2
    2    3
    3    4
    4    5
    dtype: int64
    
    In [152]: b
    Out[152]: 
    0    0
    1    1
    2    2
    3    3
    4    4
    dtype: int64
    
    In [153]: a+b
    Out[153]: 
    0    1
    1    3
    2    5
    3    7
    4    9
    dtype: int64
    复制代码
  • 索引:sr[0], sr[[1,2,4]]
    复制代码
    In [157]: b
    Out[157]: 
    a    4
    b    7
    c   -5
    d    3
    e    0
    dtype: int64
    
    In [158]: b[0]
    Out[158]: 4
    
    In [159]: b['a']
    Out[159]: 4
    复制代码
  • 切片:sr[0:2](切片依然是视图形式,浅拷贝)
    复制代码
    In [161]: b[1:3]
    Out[161]: 
    b    7
    c   -5
    dtype: int64
    
    In [162]: b['b':'d']
    Out[162]: 
    b    7
    c   -5
    d    3
    dtype: int64
    
    In [163]: b['b':'c']
    Out[163]: 
    b    7
    c   -5
    dtype: int64
    复制代码
  • 通用函数:np.abs(sr)
  • 布尔值过滤:sr[sr>0]
    复制代码
    In [164]: b
    Out[164]: 
    a    4
    b    7
    c   -5
    d    3
    e    0
    dtype: int64
    
    In [165]: b[b>3]
    Out[165]: 
    a    4
    b    7
    dtype: int64
    复制代码
  • 统计函数:mean() sum() cumsum()
    复制代码
    In [169]: b
    Out[169]: 
    a    4
    b    7
    c   -5
    d    3
    e    0
    dtype: int64
    
    In [170]: b.mean()
    Out[170]: 1.8
    
    In [171]: b.sum()
    Out[171]: 9
    
    In [173]: b.cumsum(). #每个数字与这列之前的数据的和
    Out[173]: 
    a     4
    b    11
    c     6
    d     9
    e     9
    dtype: int64
    复制代码
  • Series支持字典的特性(标签):
    • 从字典创建Series:Series(dic), 
    • in运算:’a’ in sr、for x in sr
    • 键索引:sr['a'], sr[['a', 'b', 'd']]
    • 键切片:sr['a':'c']
    • 其他函数:get('a', default=0)等

整数索引:

如果索引是整数类型,则根据整数进行数据操作时总是面向标签的。

  • loc属性 以标签解释
  • iloc属性 以下标解释 
复制代码
In [178]: sr = pd.Series(np.arange(4.))

In [179]: sr
Out[179]: 
0    0.0
1    1.0
2    2.0
3    3.0
dtype: float64

In [185]: sr.iloc[0]
Out[185]: 0.0

In [186]: sr.loc[0]
Out[186]: 0.0
复制代码

Series数据对齐

  pandas在运算时,会按索引进行对齐然后计算。如果存在不同的索引,则结果的索引是两个操作数索引的并集。

  • sr1.add(sr2, fill_value=0)
  • 灵活的算术方法:add, sub, div, mul
复制代码
In [189]: sr1
Out[189]: 
c    12
a    23
d    34
dtype: int64

In [190]: sr2
Out[190]: 
d    11
c    20
a    10
dtype: int64

In [191]: sr1+sr2
Out[191]: 
a    33
c    32
d    45
dtype: int64

In [192]: sr3 = pd.Series([11,20,10,14], index=['d','c','a','b'])

In [193]: sr2+sr3
Out[193]: 
a    20.0
b     NaN
c    40.0
d    22.0
dtype: float64


In [194]: sr2.add(sr3,fill_value = 0)
Out[194]: 
a    20.0
b    14.0
c    40.0
d    22.0
dtype: float64
复制代码
  • 缺失数据:使用NaN(Not a Number)来表示缺失数据。其值等于np.nan。内置的None值也会被当做NaN处理。
  • 处理缺失数据的相关方法:
    • dropna() 过滤掉值为NaN的行
    • fillna() 填充缺失数据
    • isnull() 返回布尔数组,缺失值对应为True
    • notnull() 返回布尔数组,缺失值对应为False
  • 过滤缺失数据:sr.dropna() 或 sr[data.notnull()]
  • 填充缺失数据:fillna(0)

DataFrame

DataFrame是一个表格型的数据结构,含有一组有序的列。

DataFrame可以被看做是由Series组成的字典,并且共用一个索引。

创建方式:

  • pd.DataFrame({'one':[1,2,3,4],'two':[4,3,2,1]})
  • pd.DataFrame({'one':pd.Series([1,2,3],index=['a','b','c']), 'two':pd.Series([1,2,3,4],index=['b','a','c','d'])})
复制代码
In [195]: df = pd.DataFrame({'one':pd.Series([1,2,3],index=['a','b','c']), 'two'
     ...: :pd.Series([1,2,3,4],index=['b','a','c','d'])})

In [196]: df
Out[196]: 
   one  two
a  1.0    2
b  2.0    1
c  3.0    3
d  NaN    4
复制代码

csv文件读取与写入:

  • df.read_csv('filename.csv')
  • df.to_csv()

常用属性及方法:

  • T 转置
  • index 获取索引
  • columns 获取列索引
  • values 获取值数组
    复制代码
    In [213]: df
    Out[213]: 
       one  two
    a  1.0    2
    b  2.0    1
    c  3.0    3
    d  NaN    4
    
    In [214]: df.index
    Out[214]: Index(['a', 'b', 'c', 'd'], dtype='object')
    
    In [215]: df.columns
    Out[215]: Index(['one', 'two'], dtype='object')
    
    In [216]: df.values
    Out[216]: 
    array([[  1.,   2.],
           [  2.,   1.],
           [  3.,   3.],
           [ nan,   4.]])
    复制代码

索引和切片:

复制代码
In [196]: df
Out[196]: 
   one  two
a  1.0    2
b  2.0    1
c  3.0    3
d  NaN    4

In [197]: df['one'] #只能是列名
Out[197]: 
a    1.0
b    2.0
c    3.0
d    NaN
Name: one, dtype: float64
复制代码
复制代码
In [221]: df
Out[221]: 
   one  two
a  1.0    2
b  2.0    1
c  3.0    3
d  NaN    4

In [222]: df.loc['a',df.columns[1]] #行和列
Out[222]: 2
复制代码

通过位置获取:

  • df.iloc[3]
  • df.iloc[3,3]
  • df.iloc[0:3,4:6]
  • df.iloc[1:5,:]
  • df.iloc[[1,2,4],[0,3]]

通过布尔值过滤:

  • df[df['A']>0]
  • df[df['A'].isin([1,3,5])]
    复制代码
    In [237]: df['one'].isin([1.0,3.0])
    Out[237]: 
    a     True
    b    False
    c     True
    d    False
    Name: one, dtype: bool
    复制代码
  • df[df<0] = 0

DataFrame数据对齐与缺失数据:

DataFrame处理缺失数据的方法:

  • dropna(axis=0, how='any',…) #默认删一行,axis = o 删行,axis = 1 删列
  • 复制代码
    In [261]: df
    Out[261]: 
       one  two    1
    a  1.0  2.0  NaN
    b  2.0  1.0  1.0
    c  3.0  3.0  NaN
    d  NaN  NaN  NaN
    3  1.0  1.0  1.0
    
    In [262]: df.dropna()
    Out[262]: 
       one  two    1
    b  2.0  1.0  1.0
    3  1.0  1.0  1.0
    
    In [263]: df.dropna(axis=1)
    Out[263]: 
    Empty DataFrame
    Columns: []
    Index: [a, b, c, d, 3]
    复制代码
  • fillna()
  • isnull()
  • notnull()
复制代码
In [223]: df
Out[223]: 
   one  two
a  1.0    2
b  2.0    1
c  3.0    3
d  NaN    4

In [224]: df.fillna(11)
Out[224]: 
    one  two
a   1.0    2
b   2.0    1
c   3.0    3
d  11.0    4
复制代码

pandas常用方法(适用Series和DataFrame):

  • mean(axis=0,skipna=False)
  • sum(axis=1)
  • sort_index(axis, …, ascending) 按行或列索引排序
  • sort_values(by, axis, ascending) 按值排序
  • NumPy的通用函数同样适用于pandas 
  • apply(func, axis=0) 将自定义函数应用在各行或者各列上 ,func可返回标量或者Series
  • applymap(func) 将函数应用在DataFrame各个元素上
  • map(func) 将函数应用在Series各个元素上

层次化索引

  • 层次化索引是Pandas的一项重要功能,它使我们能够在一个轴上拥有多个索引级别。
  • 例:data=pd.Series(np.random.rand(9), index=[['a', 'a', 'a', 'b', 'b', 'b', 'c', 'c', 'c'], [1,2,3,1,2,3,1,2,3]])

从文件读取

  • 读取文件:从文件名、URL、文件对象中加载数据
    • read_csv 默认分隔符为csv
    • read_table 默认分隔符为\t
    • read_excel 读取excel文件
  • 读取文件函数主要参数:
    • sep 指定分隔符,可用正则表达式如'\s+'
    • header=None 指定文件无列名
    • name 指定列名
    • index_col 指定某列作为索引
    • skip_row 指定跳过某些行
    • na_values 指定某些字符串表示缺失值
    • parse_dates 指定某些列是否被解析为日期,布尔值或列表
  • 写入到文件:
    • to_csv
  • 写入文件函数的主要参数:
    • sep
    • na_rep 指定缺失值转换的字符串,默认为空字符串
    • header=False 不输出列名一行
    • index=False 不输出行索引一列
  • 其他文件类型:json, XML, HTML, 数据库
  • pandas转换为二进制文件格式(pickle):
    • save
    • load

时间对象处理(常作为索引):

  • 第三方包:dateutil
    • dateutil.parser.parse()
  • 成组处理日期:pandas
    • pd.to_datetime(['2001-01-01', '2002-02-02'])
  • 产生时间对象数组:date_range
    • start 开始时间
    • end 结束时间
    • periods 时间长度
    • freq 时间频率,默认为'D',可选H(our),W(eek),B(usiness),S(emi-)M(onth),(min)T(es), S(econd), A(year),…

时间序列就是以时间对象为索引的Series或DataFrame。

  • datetime对象作为索引时是存储在DatetimeIndex对象中的。
  • 时间序列特殊功能:
    • 传入“年”或“年月”作为切片方式
    • 传入日期范围作为切片方式
复制代码
df = pd.read_csv('601318.csv',index_col='date',parse_dates=['date'],)


In [266]: df

Out[266]: 

            Unnamed: 0    open   close    high     low      volume    code

date                                                                      

2007-03-01           0  22.074  20.657  22.503  20.220  1977633.51  601318

2007-03-02           1  20.750  20.489  20.944  20.256   425048.32  601318

2007-03-05           2  20.300  19.593  20.384  19.218   419196.74  601318

2007-03-06           3  19.426  19.977  20.308  19.315   297727.88  601318

2007-03-07           4  19.995  20.520  20.706  19.827   287463.78  601318

2007-03-08           5  20.353  20.273  20.454  20.167   130983.83  601318

2007-03-09           6  20.264  20.101  20.353  19.735   160887.79  601318

2007-03-12           7  19.999  19.739  19.999  19.646   145353.06  601318

2007-03-13           8  19.783  19.818  19.982  19.699   102319.68  601318

2007-03-14           9  19.558  19.841  19.911  19.333   173306.56  601318

2007-03-15          10  20.097  19.849  20.525  19.779   152521.90  601318

2007-03-16          11  19.863  19.960  20.286  19.602   227547.24  601318

2007-03-20          12  20.662  20.211  20.715  20.088   222026.87  601318

2007-03-21          13  20.220  19.911  20.308  19.823   136728.32  601318

2007-03-22          14  20.066  20.026  20.273  19.969   167509.84  601318

2007-03-23          15  20.017  19.938  20.101  19.739   139810.14  601318

2007-03-26          16  19.955  20.282  20.397  19.946   223266.79  601318

2007-03-27          17  20.216  20.269  20.467  20.145   139338.19  601318

2007-03-28          18  20.264  20.565  20.706  20.123   258263.69  601318

复制代码
df[df['close']>df['open']] #时间索引的好处
pandas.read_csv参数整理
 
读取CSV(逗号分割)文件到DataFrame
也支持文件的部分导入和选择迭代
更多帮助参见:http://pandas.pydata.org/pandas-docs/stable/io.html
参数:
filepath_or_buffer : str,pathlib。str, pathlib.Path, py._path.local.LocalPath or any object with a read() method (such as a file handle or StringIO)
可以是URL,可用URL类型包括:http, ftp, s3和文件。对于多文件正在准备中
本地文件读取实例:://localhost/path/to/table.csv
 
sep : str, default ‘,’
指定分隔符。如果不指定参数,则会尝试使用逗号分隔。分隔符长于一个字符并且不是‘\s+’,将使用python的语法分析器。并且忽略数据中的逗号。正则表达式例子:'\r\t'
 
delimiter : str, default None
定界符,备选分隔符(如果指定该参数,则sep参数失效)
 
delim_whitespace : boolean, default False.
指定空格(例如’ ‘或者’ ‘)是否作为分隔符使用,等效于设定sep='\s+'。如果这个参数设定为Ture那么delimiter 参数失效。
在新版本0.18.1支持
 
header : int or list of ints, default ‘infer’
指定行数用来作为列名,数据开始行数。如果文件中没有列名,则默认为0,否则设置为None。如果明确设定header=0 就会替换掉原来存在列名。header参数可以是一个list例如:[0,1,3],这个list表示将文件中的这些行作为列标题(意味着每一列有多个标题),介于中间的行将被忽略掉(例如本例中的2;本例中的数据1,2,4行将被作为多级标题出现,第3行数据将被丢弃,dataframe的数据从第5行开始。)。
注意:如果skip_blank_lines=True 那么header参数忽略注释行和空行,所以header=0表示第一行数据而不是文件的第一行。
 
names : array-like, default None
用于结果的列名列表,如果数据文件中没有列标题行,就需要执行header=None。默认列表中不能出现重复,除非设定参数mangle_dupe_cols=True。
 
index_col : int or sequence or False, default None
用作行索引的列编号或者列名,如果给定一个序列则有多个行索引。
如果文件不规则,行尾有分隔符,则可以设定index_col=False 来是的pandas不适用第一列作为行索引。
 
usecols : array-like, default None
返回一个数据子集,该列表中的值必须可以对应到文件中的位置(数字可以对应到指定的列)或者是字符传为文件中的列名。例如:usecols有效参数可能是 [0,1,2]或者是 [‘foo’, ‘bar’, ‘baz’]。使用这个参数可以加快加载速度并降低内存消耗。
 
as_recarray : boolean, default False
不赞成使用:该参数会在未来版本移除。请使用pd.read_csv(...).to_records()替代。
返回一个Numpy的recarray来替代DataFrame。如果该参数设定为True。将会优先squeeze参数使用。并且行索引将不再可用,索引列也将被忽略。
 
squeeze : boolean, default False
如果文件值包含一列,则返回一个Series
 
prefix : str, default None
在没有列标题时,给列添加前缀。例如:添加‘X’ 成为 X0, X1, ...
 
mangle_dupe_cols : boolean, default True
重复的列,将‘X’...’X’表示为‘X.0’...’X.N’。如果设定为false则会将所有重名列覆盖。
 
dtype : Type name or dict of column -> type, default None
每列数据的数据类型。例如 {‘a’: np.float64, ‘b’: np.int32}
 
engine : {‘c’, ‘python’}, optional
Parser engine to use. The C engine is faster while the python engine is currently more feature-complete.
使用的分析引擎。可以选择C或者是python。C引擎快但是Python引擎功能更加完备。
 
converters : dict, default None
列转换函数的字典。key可以是列名或者列的序号。
 
true_values : list, default None
Values to consider as True
 
false_values : list, default None
Values to consider as False
 
skipinitialspace : boolean, default False
忽略分隔符后的空白(默认为False,即不忽略).
 
skiprows : list-like or integer, default None
需要忽略的行数(从文件开始处算起),或需要跳过的行号列表(从0开始)。
 
skipfooter : int, default 0
从文件尾部开始忽略。 (c引擎不支持)
 
skip_footer : int, default 0
不推荐使用:建议使用skipfooter ,功能一样。
 
nrows : int, default None
需要读取的行数(从文件头开始算起)。
 
na_values : scalar, str, list-like, or dict, default None
一组用于替换NA/NaN的值。如果传参,需要制定特定列的空值。默认为‘1.#IND’, ‘1.#QNAN’, ‘N/A’, ‘NA’, ‘NULL’, ‘NaN’, ‘nan’`.
 
keep_default_na : bool, default True
如果指定na_values参数,并且keep_default_na=False,那么默认的NaN将被覆盖,否则添加。
 
na_filter : boolean, default True
是否检查丢失值(空字符串或者是空值)。对于大文件来说数据集中没有空值,设定na_filter=False可以提升读取速度。
 
verbose : boolean, default False
是否打印各种解析器的输出信息,例如:“非数值列中缺失值的数量”等。
 
skip_blank_lines : boolean, default True
如果为True,则跳过空行;否则记为NaN。
 
parse_dates : boolean or list of ints or names or list of lists or dict, default False
  • boolean. True -> 解析索引
  • list of ints or names. e.g. If [1, 2, 3] -> 解析1,2,3列的值作为独立的日期列;
  • list of lists. e.g. If [[1, 3]] -> 合并1,3列作为一个日期列使用
  • dict, e.g. {‘foo’ : [1, 3]} -> 将1,3列合并,并给合并后的列起名为"foo"
 
infer_datetime_format : boolean, default False
如果设定为True并且parse_dates 可用,那么pandas将尝试转换为日期类型,如果可以转换,转换方法并解析。在某些情况下会快5~10倍。
 
keep_date_col : boolean, default False
如果连接多列解析日期,则保持参与连接的列。默认为False。
 
date_parser : function, default None
用于解析日期的函数,默认使用dateutil.parser.parser来做转换。Pandas尝试使用三种不同的方式解析,如果遇到问题则使用下一种方式。
1.使用一个或者多个arrays(由parse_dates指定)作为参数;
2.连接指定多列字符串作为一个列作为参数;
3.每行调用一次date_parser函数来解析一个或者多个字符串(由parse_dates指定)作为参数。
 
dayfirst : boolean, default False
DD/MM格式的日期类型
 
iterator : boolean, default False
返回一个TextFileReader 对象,以便逐块处理文件。
 
chunksize : int, default None
文件块的大小, See IO Tools docs for more informationon iterator and chunksize.
 
compression : {‘infer’, ‘gzip’, ‘bz2’, ‘zip’, ‘xz’, None}, default ‘infer’
直接使用磁盘上的压缩文件。如果使用infer参数,则使用 gzip, bz2, zip或者解压文件名中以‘.gz’, ‘.bz2’, ‘.zip’, or ‘xz’这些为后缀的文件,否则不解压。如果使用zip,那么ZIP包中国必须只包含一个文件。设置为None则不解压。
新版本0.18.1版本支持zip和xz解压
 
thousands : str, default None
千分位分割符,如“,”或者“."
 
decimal : str, default ‘.’
字符中的小数点 (例如:欧洲数据使用’,‘).
 
float_precision : string, default None
Specifies which converter the C engine should use for floating-point values. The options are None for the ordinary converter, high for the high-precision converter, and round_trip for the round-trip converter.
指定
 
lineterminator : str (length 1), default None
行分割符,只在C解析器下使用。
 
quotechar : str (length 1), optional
引号,用作标识开始和解释的字符,引号内的分割符将被忽略。
 
quoting : int or csv.QUOTE_* instance, default 0
控制csv中的引号常量。可选 QUOTE_MINIMAL (0), QUOTE_ALL (1), QUOTE_NONNUMERIC (2) or QUOTE_NONE (3)
 
doublequote : boolean, default True
双引号,当单引号已经被定义,并且quoting 参数不是QUOTE_NONE的时候,使用双引号表示引号内的元素作为一个元素使用。
 
escapechar : str (length 1), default None
当quoting 为QUOTE_NONE时,指定一个字符使的不受分隔符限值。
 
comment : str, default None
标识着多余的行不被解析。如果该字符出现在行首,这一行将被全部忽略。这个参数只能是一个字符,空行(就像skip_blank_lines=True)注释行被header和skiprows忽略一样。例如如果指定comment='#' 解析‘#empty\na,b,c\n1,2,3’ 以header=0 那么返回结果将是以’a,b,c'作为header。
 
encoding : str, default None
指定字符集类型,通常指定为'utf-8'. List of Python standard encodings
 
dialect : str or csv.Dialect instance, default None
如果没有指定特定的语言,如果sep大于一个字符则忽略。具体查看csv.Dialect 文档
 
tupleize_cols : boolean, default False
Leave a list of tuples on columns as is (default is to convert to a Multi Index on the columns)
 
error_bad_lines : boolean, default True
如果一行包含太多的列,那么默认不会返回DataFrame ,如果设置成false,那么会将改行剔除(只能在C解析器下使用)。
 
warn_bad_lines : boolean, default True
如果error_bad_lines =False,并且warn_bad_lines =True 那么所有的“bad lines”将会被输出(只能在C解析器下使用)。
 
low_memory : boolean, default True
分块加载到内存,再低内存消耗中解析。但是可能出现类型混淆。确保类型不被混淆需要设置为False。或者使用dtype 参数指定类型。注意使用chunksize 或者iterator 参数分块读入会将整个文件读入到一个Dataframe,而忽略类型(只能在C解析器中有效)
 
buffer_lines : int, default None
不推荐使用,这个参数将会在未来版本移除,因为他的值在解析器中不推荐使用
 
compact_ints : boolean, default False
不推荐使用,这个参数将会在未来版本移除
如果设置compact_ints=True ,那么任何有整数类型构成的列将被按照最小的整数类型存储,是否有符号将取决于use_unsigned 参数
 
use_unsigned : boolean, default False
不推荐使用:这个参数将会在未来版本移除
如果整数列被压缩(i.e. compact_ints=True),指定被压缩的列是有符号还是无符号的。
memory_map : boolean, default False
如果使用的文件在内存内,那么直接map文件使用。使用这种方式可以避免文件再次进行IO操作。
posted on 2018-08-06 16:48  法海降妖  阅读(224)  评论(0编辑  收藏  举报