1. 合并

可以将其理解为SQL中的JOIN操作,使用一个或多个键把多行数据结合在一起。

1.1. 简单合并

参数on表示合并依据的列,参数how表示用什么方式操作(默认是内连接)。

>>> frame1 = pd.DataFrame( {'id':['ball', 'pencil', 'pen', 'mug', 'ashtray'], 'color':['white', 'red', 'red', 'black', 'green'], 'brand':['OMG', 'ABC', 'ABC', 'POD', 'POD']} )
>>> frame1
brand color id
0 OMG white ball
1 ABC red pencil
2 ABC red pen
3 POD black mug
4 POD green ashtray
>>> frame2 = pd.DataFrame( {'id':['pencil', 'pencil', 'ball', 'pen'], 'brand':['OMG', 'POD', 'ABC', 'POD']} )
>>> frame2
brand id
0 OMG pencil
1 POD pencil
2 ABC ball
3 POD pen
>>> frame2.columns = ['brand', 'nid'] 可以指定左边用哪一列合并,右边用哪一列合并
>>> pd.merge(frame1, frame2, left_on='id', right_on='nid')
brand_x color id brand_y nid
0 OMG white ball ABC ball
1 ABC red pencil OMG pencil
2 ABC red pencil POD pencil
3 ABC red pen POD pen 根据多个列进行,并且以外连接方式合并
>>> frame2.columns = ['brand', 'id']
>>> pd.merge(frame1, frame2, on=['id', 'brand'], how='outer')
brand color id
0 OMG white ball
1 ABC red pencil
2 ABC red pen
3 POD black mug
4 POD green ashtray
5 OMG NaN pencil
6 POD NaN pencil
7 ABC NaN ball
8 POD NaN pen

1.2. 根据索引合并

将left_index和right_index设为True
>>> pd.merge(frame1, frame2, right_index=True, left_index=True)
brand_x color id_x brand_y id_y
0 OMG white ball OMG pencil
1 ABC red pencil POD pencil
2 ABC red pen ABC ball
3 POD black mug POD pen 使用DataFrame()对象的join()函数进行合并,注意不能有重复的列名
>>> frame2.columns=['brand2', 'id2']
>>> frame1.join(frame2)
brand color id brand2 id2
0 OMG white ball OMG pencil
1 ABC red pencil POD pencil
2 ABC red pen ABC ball
3 POD black mug POD pen
4 POD green ashtray NaN NaN

2. 拼接

2.1. NumPy的concatenate()函数

>>> array1 = np.arange(9).reshape((3,3))
>>> array2 = np.arange(9).reshape((3,3))+3
>>> array1
array([[0, 1, 2],
[3, 4, 5],
[6, 7, 8]])
>>> array2
array([[ 3, 4, 5],
[ 6, 7, 8],
[ 9, 10, 11]])
>>> np.concatenate([array1,array2])
array([[ 0, 1, 2],
[ 3, 4, 5],
[ 6, 7, 8],
[ 3, 4, 5],
[ 6, 7, 8],
[ 9, 10, 11]])
>>> np.concatenate([array1,array2], axis = 1)
array([[ 0, 1, 2, 3, 4, 5],
[ 3, 4, 5, 6, 7, 8],
[ 6, 7, 8, 9, 10, 11]])

2.2. Pandas的concat()函数

>>> ser1
1 0.105168
2 0.344241
3 0.798570
4 0.648794
dtype: float64
>>> ser2
5 0.161205
6 0.427794
7 0.813935
8 0.742369
dtype: float64
>>> pd.concat([ser1, ser2])
1 0.105168
2 0.344241
3 0.798570
4 0.648794
5 0.161205
6 0.427794
7 0.813935
8 0.742369
dtype: float64
>>> pd.concat([ser1, ser2], axis = 1)
0 1
1 0.105168 NaN
2 0.344241 NaN
3 0.798570 NaN
4 0.648794 NaN
5 NaN 0.161205
6 NaN 0.427794
7 NaN 0.813935
8 NaN 0.742369
由上面可以看出concat()默认选项是外连接. 指定为内连接
>>> ser3 = pd.Series(np.random.rand(4), index = [1,2,3,4])
>>> ser3
1 0.599582
2 0.123096
3 0.870401
4 0.635363
dtype: float64
>>> pd.concat([ser1, ser3], axis = 1, join = 'inner')
0 1
1 0.105168 0.599582
2 0.344241 0.123096
3 0.798570 0.870401
4 0.648794 0.635363 可以用keys参数可以创建等级索引,对列进行合并的话,keys会变成列名
>>> pd.concat([ser1, ser2], keys=['fir','sec'])
fir 1 0.105168
2 0.344241
3 0.798570
4 0.648794
sec 5 0.161205
6 0.427794
7 0.813935
8 0.742369
dtype: float64
>>> pd.concat([ser1, ser2], axis=1, keys=['fir','sec'])
fir sec
1 0.105168 NaN
2 0.344241 NaN
3 0.798570 NaN
4 0.648794 NaN
5 NaN 0.161205
6 NaN 0.427794
7 NaN 0.813935
8 NaN 0.742369

3. 组合

使用combine_first()函数。

>>> ser1 = pd.Series(np.random.rand(5), index = [1,2,3,4,5])
>>> ser2 = pd.Series(np.random.rand(5), index = [1,3,5,7,9])
>>> ser1
1 0.426299
2 0.732439
3 0.951809
4 0.955885
5 0.183026
dtype: float64
>>> ser2
1 0.981828
3 0.162136
5 0.364654
7 0.607903
9 0.345988
dtype: float64
>>> ser1.combine_first(ser2)
1 0.426299
2 0.732439
3 0.951809
4 0.955885
5 0.183026
7 0.607903
9 0.345988
dtype: float64
>>> ser2.combine_first(ser1)
1 0.981828
2 0.732439
3 0.162136
4 0.955885
5 0.364654
7 0.607903
9 0.345988
dtype: float64 部分合并使用切片
>>> ser1[:4].combine_first(ser2[:4])
1 0.426299
2 0.732439
3 0.951809
4 0.955885
5 0.364654
7 0.607903
dtype: float64

4. 轴向旋转

4.1. 按等级索引旋转

stack():把列转换成行。

unstack():把行转换成列。

>>> frame1 = pd.DataFrame(np.arange(9).reshape(3,3), index=['white', 'black', 'red'], columns=['ball', 'pen', 'pencil'])
>>> frame1
ball pen pencil
white 0 1 2
black 3 4 5
red 6 7 8 >>> ser = frame1.stack()
>>> ser
white ball 0
pen 1
pencil 2
black ball 3
pen 4
pencil 5
red ball 6
pen 7
pencil 8
dtype: int32 >>> ser = frame1.unstack()
>>> ser
ball white 0
black 3
red 6
pen white 1
black 4
red 7
pencil white 2
black 5
red 8
dtype: int32 >>> ser.unstack(0)
ball pen pencil
white 0 1 2
black 3 4 5
red 6 7 8
>>> ser.unstack(1)
white black red
ball 0 3 6
pen 1 4 7
pencil 2 5 8
参数表示对第几层进行操作

4.2. 从"长"向"宽"旋转

有时候一类数据集各列都有数据项,每一列后面的数据和前面有重复,这类数据常为列表形式,将其称为长格式栈格式。还有一种宽格式,可读性强,存储数据效率更高。因此有时候需要转换。

>>> longframe = pd.DataFrame( {'color':['white', 'white', 'white', 'red', 'red', 'red', 'black', 'black', 'black'], 'item':['ball','pen','mug','ball','pen','mug','ball','pen','mug'],'value':np.random.rand(9)} )
>>> longframe
color item value
0 white ball 0.657363
1 white pen 0.209334
2 white mug 0.878001
3 red ball 0.674560
4 red pen 0.278861
5 red mug 0.393061
6 black ball 0.956869
7 black pen 0.217121
8 black mug 0.611301 >>> wideframe = longframe.pivot('color', 'item')
>>> wideframe
value
item ball mug pen
color
black 0.956869 0.611301 0.217121
red 0.674560 0.393061 0.278861
white 0.657363 0.878001 0.209334 >>> longframe = pd.DataFrame( {'color':['white', 'white', 'white', 'red', 'red', 'red', 'black', 'black', 'black'], 'item':['ball','pen','mug','ball','pen','mug','ball','pen','mug'],'value':np.random.rand(9), 'game':np.arange(9)} )
>>> wideframe = longframe.pivot('color', 'item')
>>> wideframe
game value
item ball mug pen ball mug pen
color
black 6 8 7 0.122732 0.820089 0.505179
red 3 5 4 0.314163 0.964050 0.670957
white 0 2 1 0.219532 0.665007 0.833881

使用pivot()函数指定两个键,第一个参数的列变成索引名,第二个参数的列变成列名。这样数据的可读性就很高了。

5. 数据转换

5.1. 删除重复元素

使用duplicated()可以获取哪些是重复的,使用drop_duplicates()能够删除重复元素。

>>> frame = pd.DataFrame({'color':['white', 'white', 'red', 'red', 'white'], 'value':[1,2,3,3,2]})
>>> frame
color value
0 white 1
1 white 2
2 red 3
3 red 3
4 white 2
>>> frame.duplicated()
0 False
1 False
2 False
3 True
4 True
dtype: bool
>>> frame[frame.duplicated()]
color value
3 red 3
4 white 2
>>> frame.drop_duplicates()
color value
0 white 1
1 white 2
2 red 3

5.2. 映射

5.2.1. 替换元素replace()

使用replace()可以替换元素。可以传入一个字典,键为旧元素,值为新元素。

>>> frame
color value
0 white 1
1 white 2
2 red 3
3 red 3
4 white 2
>>> mp = {'white': 'black', 'red': 'yellow'}
>>> frame.replace(mp)
color value
0 black 1
1 black 2
2 yellow 3
3 yellow 3
4 black 2
>>> frame.replace('white', 'black')
color value
0 black 1
1 black 2
2 red 3
3 red 3
4 black 2

5.2.2. 添加元素map()

使用map()可以从另外一个数据结构获取元素并将其添加到目标数据结构的列中。

>>> frame
color value
0 white 1
1 white 2
2 red 3
3 red 3
4 white 2
>>> dic = {'white':'pure', 'red':'ignite'} >>> frame['live'] = frame['color'].map(dic)
>>> frame
color value live
0 white 1 pure
1 white 2 pure
2 red 3 ignite
3 red 3 ignite
4 white 2 pure

5.2.3. 重命名轴索引rename()

使用rename()转换轴标签。

>>> frame
color value live
0 white 1 pure
1 white 2 pure
2 red 3 ignite
3 red 3 ignite
4 white 2 pure
>>> newindex = {0:'zero', 3:'three'}
>>> newcolumns = {'color':'col', 'value':'val'}
>>> frame.rename(index = newindex, columns = newcolumns)
col val live
zero white 1 pure
1 white 2 pure
2 red 3 ignite
three red 3 ignite
4 white 2 pure # 如果要将原来的frame替换掉,设置inplace参数为True
>>> frame.rename(index = newindex, columns = newcolumns, inplace = True)
>>> frame
col val live
zero white 1 pure
1 white 2 pure
2 red 3 ignite
three red 3 ignite
4 white 2 pure

6. 离散化和面元划分

6.1. cut()

cut()函数可以将一个数组中的数据切分成几个部分。

将数据分为几个部分,就称为几个面元。

cut(数据数组,面元数组)

>>> array = [3, 60, 43, 100, 52, 36, 37, 0, 80, 1000] # 数组
>>> bins = [0, 25, 50, 75, 100] # 切割成什么
>>> cat = pd.cut(array, bins)
>>> cat # cat是一个Categorical(类别型)类型
[(0, 25], (50, 75], (25, 50], (75, 100], (50, 75], (25, 50], (25, 50], NaN, (75, 100], NaN]
Categories (4, interval[int64]): [(0, 25] < (25, 50] < (50, 75] < (75, 100]]
>>> cat.codes # 数组原来的元素数据第几个面元
array([ 0, 2, 1, 3, 2, 1, 1, -1, 3, -1], dtype=int8)
>>> pd.value_counts(cat) # 每个面元有多少个元素
(25, 50] 3
(75, 100] 2
(50, 75] 2
(0, 25] 1
dtype: int64

还可以不指定面元的界限,直接传入一个整数参数,cut()会按照指定的数字,将元素划分为相应的几部分。

>>> pd.cut(array, 5)
[(-1.0, 200.0], (-1.0, 200.0], (-1.0, 200.0], (-1.0, 200.0], (-1.0, 200.0], (-1.0, 200.0], (-1.0, 200.0], (-1.0, 200.0], (-1.0, 200.0], (800.0, 1000.0]]
Categories (5, interval[float64]): [(-1.0, 200.0] < (200.0, 400.0] < (400.0, 600.0] < (600.0, 800.0] < (800.0, 1000.0]]

6.2. qcut()

cut()函数划分得到的面元,每个面元的数量不同。而qcut()可以保证每个面元的数量相同,且每个面元的区间大小不等。

>>> pd.qcut(array, 5)
[(-0.001, 29.4], (55.2, 84.0], (40.6, 55.2], (84.0, 1000.0], (40.6, 55.2], (29.4, 40.6], (29.4, 40.6], (-0.001, 29.4], (55.2, 84.0], (84.0, 1000.0]]
Categories (5, interval[float64]): [(-0.001, 29.4] < (29.4, 40.6] < (40.6, 55.2] < (55.2, 84.0] < (84.0, 1000.0]]
>>> cat = pd.qcut(array, 5)
>>> pd.value_counts(cat)
(84.0, 1000.0] 2
(55.2, 84.0] 2
(40.6, 55.2] 2
(29.4, 40.6] 2
(-0.001, 29.4] 2
dtype: int64

6.3. 异常值检测和过滤

使用any()函数可以对每一列应用筛选条件。

>>> frame = pd.DataFrame(np.random.randn(10, 3))
>>> frame
0 1 2
0 -0.466623 -0.180515 -1.632522
1 0.928587 1.478555 -1.170217
2 1.366825 -0.266165 0.307137
3 1.811664 0.155917 -1.847898
4 -0.451448 1.668134 -0.584497
5 -0.819943 -0.028708 1.119363
6 0.039233 -0.316006 -1.232731
7 -2.721860 0.369594 0.482038
8 -0.320213 -0.456954 1.305954
9 -0.159289 -1.138182 0.452671 >>> frame.describe()
0 1 2
count 10.000000 10.000000 10.000000
mean -0.079307 0.128567 -0.280070
std 1.272859 0.861058 1.155481
min -2.721860 -1.138182 -1.847898
25% -0.462829 -0.303546 -1.217102
50% -0.239751 -0.104611 -0.138680
75% 0.706248 0.316174 0.474696
max 1.811664 1.668134 1.305954 >>> (np.abs(frame) > frame.std())
0 1 2
0 False False True
1 False True True
2 True False False
3 True False True
4 False True False
5 False False False
6 False False True
7 True False False
8 False False True
9 False True False >>> (np.abs(frame) > frame.std()).any(0) # 参数0和参数1分别是axis参数的值
0 True
1 True
2 True >>> (np.abs(frame) > frame.std()).any(1)
0 True
1 True
2 True
3 True
4 True
5 False
6 True
7 True
8 True
9 True
dtype: bool >>> frame[(np.abs(frame) > frame.std()).any(1)] # 第五行被筛选掉了
0 1 2
0 -0.466623 -0.180515 -1.632522
1 0.928587 1.478555 -1.170217
2 1.366825 -0.266165 0.307137
3 1.811664 0.155917 -1.847898
4 -0.451448 1.668134 -0.584497
6 0.039233 -0.316006 -1.232731
7 -2.721860 0.369594 0.482038
8 -0.320213 -0.456954 1.305954
9 -0.159289 -1.138182 0.452671

7. 排序

使用permutation()函数可以创建一个随机顺序的数组。

使用take()函数可以采用新的索引次序。

>>> frame = pd.DataFrame(np.arange(25).reshape(5,5))
>>> frame
0 1 2 3 4
0 0 1 2 3 4
1 5 6 7 8 9
2 10 11 12 13 14
3 15 16 17 18 19
4 20 21 22 23 24
>>> order = np.random.permutation(5)
>>> order
array([2, 4, 1, 0, 3])
>>> frame.take(order)
0 1 2 3 4
2 10 11 12 13 14
4 20 21 22 23 24
1 5 6 7 8 9
0 0 1 2 3 4
3 15 16 17 18 19 # 对部分操作
>>> frame.take([3, 4, 1])
0 1 2 3 4
3 15 16 17 18 19
4 20 21 22 23 24
1 5 6 7 8 9

随机取样

>>> len(frame) # frame的行数
5
>>> sample = np.random.randint(0, len(frame), size = 2) # 分别是取值下限,取值上限,和取值的个数
>>> sample
array([1, 1])
>>> frame.take(sample)
0 1 2 3 4
1 5 6 7 8 9
1 5 6 7 8 9

8. 字符串处理

  • join()

    '拼接字符'.join(string数组)

>>> strings = ['a', 'b', 'd', 'e', 'F']
>>> strings
['a', 'b', 'd', 'e', 'F']
>>> ';'.join(strings)
'a;b;d;e;F'
  • split() 和 strip()

    string.split('切割字符')

    string.strip() 去掉多余的空白字符(包括换行)

>>> string = '233 ,     546'
>>> string
'233 , \t546'
>>> string.split(',')
['233 ', ' \t546'] >>> for s in string.split(','):
... print(s.strip())
...
233
546
  • find(s) 和 count(s) 和 replace(s1, s2)

    分别是找到子串的下标,子串出现了多少次,将s1替换为s2.

  • 正则表达式

    click here

9. 数据聚合

GroupBy 内部机制:SPLIT-APPLY-COMBINE(分组-用函数处理-合并结果)。

group[list1].groupby(list2)

list1表示要处理的数据的列名,list2表示根据哪些键(列名)处理。

>>> frame = pd.DataFrame( {'color': ['red', 'white', 'red', 'green', 'white'], 'object': ['pen', 'pencil', 'mug', 'ruler', 'pen'], 'price': [2, 3, 4, 1, 100]} )
>>> frame
color object price
0 red pen 2
1 white pencil 3
2 red mug 4
3 green ruler 1
4 white pen 100
>>> group = frame['price'].groupby(frame['color']) # 得到一个GroupBy对象
>>> group.groups
{'red': Int64Index([0, 2], dtype='int64'), 'green': Int64Index([3], dtype='int64'), 'white': Int64Index([1, 4], dtype='int64')}
>>> group.mean()
color
green 1.0
red 3.0
white 51.5
Name: price, dtype: float64
>>> group.sum()
color
green 1
red 6
white 103
Name: price, dtype: int64

  • agg()函数

    还可以对组使用自定义函数,只需要使用agg(函数名)就行了。

>>> group = frame.groupby(frame['color'])
>>> def fun(series):
... return series.max() - series.min()
...
>>> group['price'].agg(fun)
color
green 0
red 2
white 97
Name: price, dtype: int64
  • transfrom()函数
>>> frame = pd.DataFrame( {'color':['white', 'red', 'green', 'white', 'green'], 'val1':[5,6,7,8,9], 'val2':[15,16,17,18,19]} )
>>> frame
color val1 val2
0 white 5 15
1 red 6 16
2 green 7 17
3 white 8 18
4 green 9 19
>>> frame.groupby('color').transform(np.sum) # 函数必须是一个聚合函数
val1 val2
0 13 33
1 6 16
2 16 36
3 13 33
4 16 36
  • apply()函数

    将键值分类再处理。

>>> frame['status'] = ['up', 'down', 'up', 'down', 'down']
>>> frame
color val1 val2 status
0 white 5 15 up
1 red 6 16 down
2 green 7 17 up
3 white 8 18 down
4 green 9 19 down
>>> frame.groupby(['color', 'status']).apply(lambda x: x.max())
color val1 val2 status
color status
green down green 9 19 down
up green 7 17 up
red down red 6 16 down
white down white 8 18 down
up white 5 15 up

Python之Pandas库学习(三):数据处理的更多相关文章

  1. Python之Pandas库学习(二):数据读写

    1. I/O API工具 读取函数 写入函数 read_csv to_csv read_excel to_excel read_hdf to_hdf read_sql to_sql read_json ...

  2. Python之Pandas库学习(一):简介

    官方文档 1. 安装Pandas windos下cmd:pip install pandas 导入pandas包:import pandas as pd 2. Series对象 带索引的一维数组 创建 ...

  3. python的pandas库学习笔记

    导入: import pandas as pd from pandas import Series,DataFrame 1.两个主要数据结构:Series和DataFrame (1)Series是一种 ...

  4. Struts2框架学习(三) 数据处理

    Struts2框架学习(三) 数据处理 Struts2框架框架使用OGNL语言和值栈技术实现数据的流转处理. 值栈就相当于一个容器,用来存放数据,而OGNL是一种快速查询数据的语言. 值栈:Value ...

  5. Python之Pandas库常用函数大全(含注释)

    前言:本博文摘抄自中国慕课大学上的课程<Python数据分析与展示>,推荐刚入门的同学去学习,这是非常好的入门视频. 继续一个新的库,Pandas库.Pandas库围绕Series类型和D ...

  6. pandas库学习笔记(二)DataFrame入门学习

    Pandas基本介绍——DataFrame入门学习 前篇文章中,小生初步介绍pandas库中的Series结构的创建与运算,今天小生继续“死磕自己”为大家介绍pandas库的另一种最为常见的数据结构D ...

  7. python爬虫解析库学习

    一.xpath库使用: 1.基本规则: 2.将文件转为HTML对象: html = etree.parse('./test.html', etree.HTMLParser()) result = et ...

  8. Python的Pandas库简述

    pandas 是 python 的数据分析处理库import pandas as pd 1.读取CSV.TXT文件 foodinfo = pd.read_csv("pandas_study. ...

  9. Python之matplotlib库学习:实现数据可视化

    1. 安装和文档 pip install matplotlib 官方文档 为了方便显示图像,还使用了ipython qtconsole方便显示.具体怎么弄网上搜一下就很多教程了. pyplot模块是提 ...

随机推荐

  1. SAP HR工资配置项1---工资计算周期配置

    对于工资计算,三个方面需要配置:工资计算期.工资类型.工资. 下面是工资期内的配置: 1.在定义参数 在参数指示工资的频率. 主题 设置期间參数 菜单路径 SAP 用户化实施指南→工资核算→工资核算: ...

  2. WPF依赖属性对内存的使用方式

    WPF允许对象在创建时候并不包含存储数据的空间,只保留在用到时获取数据默认值,借用其他对象数据或者实时分配空间的能力

  3. IOS开发之把 JSON 数据转化成 Arrays 或者 Dictionaries

    1 前言通过 NSJSONSerialization 这个类的 JSONObjectWithData:options:error:方法来实现,把JSON 数据解析出来放在数据或者字典里面保存. 2 代 ...

  4. 完美实现鼠标拖拽事件,解决各种小bug,基于jquery

    鼠标拖拽事件是web中使用频率极高的事件,之前写过的代码包括网上的代码,总存在各种各样的问题,包括拖拽体验差,松开鼠标后拖拽效果仍存在以及代码冗余过大等 本次我才用jQuery实现一个尽可能高效的拖拽 ...

  5. C#代码中设置 控件的触发器

    Style style = new Style(); style.TargetType = typeof(TextBox); MultiDataTrigger trigger = new MultiD ...

  6. 2018-4-25-- 2.在sublime3里安装git插件并连接GitHub

    1.配置全局参数 Git的主要配置包括用户名.邮箱的设置.以及生成SSH密钥公钥等. 首先运行一下的命令设置git提交代码时自己的用户信息. 2.在sublime3里使用时需要配置push.defau ...

  7. SQLServer 远程服务器不存在,未被指定为有效的发布服务器,或您无权查看可用的发布服务器

    原文:SQLServer 远程服务器不存在,未被指定为有效的发布服务器,或您无权查看可用的发布服务器 创建了事务发布,在初始化时出现错误,查看相关代理信息如下: 日志读取器代理错误: 状态: 0,代码 ...

  8. CS224n笔记一:开端

    何为自然语言处理 自然语言处理的目标是让计算机处理或者"理解"自然语言,以完成有意义的任务,如QA等. 自然语言处理涉及的层次 输入有两个来源:语音和文本,所以第一级是语音识别,O ...

  9. 使用网盘(Dropbox/Google Drive)同步Git仓库

    还在使用老掉牙的U盘搬运代码(文件)的方式,从一台机器上复制后,粘贴到另一台机器上?太Out了.使用Github 倒是一个非常不错的替代方法.但无论是基于什么理由都有可能不想把代码公开(毕竟Githu ...

  10. 每一位想有所成就的程序员都必须知道的15件事(走不一样的路,要去做,实践实践再实践,推销自己,关注市场)good

    从 为之漫笔作者:为之漫笔 有超过 100 人喜欢此条目 原文地址:How to advance your career? Read the Passionate Programmer! 我刚看完Ch ...