pandas学习笔记(九)
一、cat对象
1.cat对象的属性
在 pandas 中提供了 category 类型,使用户能够处理分类类型的变量,将一个普通序列转换成分类变量可以使用 astype 方法:
df = pd.read_csv('./learn_pandas.csv',
...: usecols = ['Grade','Name','Gender','Height','Weight'])
s = df.Grade.astype('category')
s.head()
Out[5]:
0 Freshman
1 Freshman
2 Senior
3 Sophomore
4 Sophomore
Name: Grade, dtype: category
Categories (4, object): ['Freshman', 'Junior', 'Senior', 'Sophomore']
对于一个具体的分类,有两个组成部分:
-
其一为类别的本身,它以Index类型储存
-
其二为是否有序,它们都可以通过cat的属性被访问
s.cat.categories Out[7]: Index(['Freshman', 'Junior', 'Senior', 'Sophomore'], dtype='object') s.cat.ordered Out[8]: False
每一个序列的类别会被赋予唯一的整数编号,它们的编号取决于 cat.categories 中的顺序,该属性可以通过 codes 访问:
s.cat.codes.head()
Out[9]:
0 0
1 0
2 2
3 3
4 3
dtype: int8
2.类别的增加、删除和修改
通过 cat 对象的 categories 属性能够完成对类别的查询,那么应该如何进行“增改查删”的其他三个操作呢?
-
对于类别的增加可以使用add_categories:
s = s.cat.add_categories('Graduate') # 增加一个毕业生类别 s.cat.categories Out[11]: Index(['Freshman', 'Junior', 'Senior', 'Sophomore', 'Graduate'], dtype='object')
-
删除某一个类别可以使用remove_categories,同时所有原来序列中的该类会被设置为缺失:
s = s.cat.remove_categories('Freshman') # 删除大一类别 s.cat.categories Out[13]: Index(['Junior', 'Senior', 'Sophomore', 'Graduate'], dtype='object') s.head() Out[14]: 0 NaN 1 NaN 2 Senior 3 Sophomore 4 Sophomore Name: Grade, dtype: category Categories (4, object): ['Junior', 'Senior', 'Sophomore', 'Graduate']
-
可以使用set_categories直接设置序列的新类别,原来的类别中如果存在元素不属于新类别,那么会被设置为缺失:
s = s.cat.set_categories(['Sophomore','PhD']) # 新类别为大二学生和博士 s.cat.categories Out[18]: Index(['Sophomore', 'PhD'], dtype='object') s.head() Out[19]: 0 NaN 1 NaN 2 NaN 3 Sophomore 4 Sophomore Name: Grade, dtype: category Categories (2, object): ['Sophomore', 'PhD']
-
可以用rmove_unused_categories删除未出现在序列中的类别:
s = s.cat.remove_unused_categories() # 删除了未出现过的博士类别 s.cat.categories Out[20]: Index(['Sophomore'], dtype='object')
-
rename_categories可以使用来对类别名进行修改。同时需要注意的是,这个方法会对原序列的对应值也进行相应修改:
s = s.cat.rename_categories({'Sophomore':'本科二年级学生'}) s.head() Out[22]: 0 NaN 1 NaN 2 NaN 3 本科二年级学生 4 本科二年级学生 Name: Grade, dtype: category Categories (1, object): ['本科二年级学生']
二、有序分类
1.序的建立
有序类别和无序类别之间的相互转化:
-
as_unordered
-
reorder_categories,传入的参数必须是由当前序列的无需类别构成的列表,不能够增加新的类别,也不能缺少原来的类别,并且必须指定参数 ordered=True ,否则方法无效:
# 例如,对年级高低进行相对大小的类别划分,然后再恢复无序状态: s = df.Grade.astype('category') s = s.cat.reorder_categories(['Freshman','Sophomore', ...: 'Junior','Senior'],ordered=True) s.head() Out[25]: 0 Freshman 1 Freshman 2 Senior 3 Sophomore 4 Sophomore Name: Grade, dtype: category Categories (4, object): ['Freshman' < 'Sophomore' < 'Junior' < 'Senior'] s.cat.as_unordered().head() Out[26]: 0 Freshman 1 Freshman 2 Senior 3 Sophomore 4 Sophomore Name: Grade, dtype: category Categories (4, object): ['Freshman', 'Sophomore', 'Junior', 'Senior'] s = s.cat.reorder_categories(['Freshman','Sophomore', ...: 'Junior','Senior'],ordered=False) s.head() Out[27]: 0 Freshman 1 Freshman 2 Senior 3 Sophomore 4 Sophomore Name: Grade, dtype: category Categories (4, object): ['Freshman', 'Sophomore', 'Junior', 'Senior'] # 当order=False时,reorder_categories生成的时无序类别,并且order的默认值也为False
2.排序和比较
分类变量的排序:只需把列的类型修改为 category 后,再赋予相应的大小关系,就能正常地使用 sort_index 和 sort_values :
# 例如,对年级进行排序:
df.Grade = df.Grade.astype('category')
df.Grade = df.Grade.cat.reorder_categories(['Freshman',
...: 'Sophomore',
...: 'Junior',
...: 'Senior'],ordered=True)
df.sort_values('Grade').head() # 值排序
Out[34]:
Grade Name Gender Height Weight
0 Freshman Gaopeng Yang Female 158.9 46.0
105 Freshman Qiang Shi Female 164.5 52.0
96 Freshman Changmei Feng Female 163.8 56.0
88 Freshman Xiaopeng Han Female 164.1 53.0
81 Freshman Yanli Zhang Female 165.1 52.0
df.set_index('Grade').sort_index().head() # 索引排序
Out[37]:
Name Gender Height Weight
Grade
Freshman Gaopeng Yang Female 158.9 46.0
Freshman Qiang Shi Female 164.5 52.0
Freshman Changmei Feng Female 163.8 56.0
Freshman Xiaopeng Han Female 164.1 53.0
Freshman Yanli Zhang Female 165.1 52.0
分类变量的比较操作分为两类:
- 第一种是 == 或 != 关系的比较,比较的对象可以是标量或者同长度的 Series (或 list )
- 第二种是 >,>=,<,<= 四类大小关系的比较,比较的对象和第一种类似,但是所有参与比较的元素必须属于原序列的 categories ,同时要和原序列具有相同的索引
res1 = df.Grade == 'Sophomore'
res1.head()
Out[39]:
0 False
1 False
2 False
3 True
4 True
Name: Grade, dtype: bool
res2 = df.Grade == ['PhD']*df.shape[0]
res2.head()
Out[41]:
0 False
1 False
2 False
3 False
4 False
Name: Grade, Length: 200, dtype: bool
res3 = df.Grade <= 'Sophomore'
res3.head()
Out[46]:
0 True
1 True
2 False
3 True
4 True
Name: Grade, Length: 200, dtype: bool
res4 = df.Grade <= df.Grade.sample(frac=1).reset_index(drop=True) # 打乱后比较
res4.head()
Out[48]:
0 True
1 True
2 False
3 True
4 True
Name: Grade, dtype: bool
三、区间类别
1.利用cut和qcut进行区间构造
-
cut:
最重要的参数为**bin**,如果传入整数 n ,则代表把整个传入数组的按照最大和最小值等间距地分为 n 段。
s = pd.Series([1,2]) pd.cut(s,bins=2) Out[51]: 0 (0.999, 1.5] 1 (1.5, 2.0] dtype: category Categories (2, interval[float64]): [(0.999, 1.5] < (1.5, 2.0]] pd.cut(s,bins=2,right=False) Out[52]: 0 [1.0, 1.5) 1 [1.5, 2.001) dtype: category Categories (2, interval[float64]): [[1.0, 1.5) < [1.5, 2.001)]
bins 的另一个常见用法是指定区间分割点的列表(使用 np.infty 可以表示无穷大):
pd.cut(s,bins=[-np.infty,1.2,1.8,2.2,np.infty]) Out[53]: 0 (-inf, 1.2] 1 (1.8, 2.2] dtype: category Categories (4, interval[float64]): [(-inf, 1.2] < (1.2, 1.8] < (1.8, 2.2] < (2.2, inf]]
另外两个常用参数为 labels 和 retbins ,分别代表了区间的名字和是否返回分割点(默认不返回):
s = pd.Series([1,2]) res = pd.cut(s,bins=2,labels=['small','big'],retbins=True) res[0] Out[56]: 0 small 1 big dtype: category Categories (2, object): ['small' < 'big'] s Out[57]: 0 1 1 2 dtype: int64 res[1] # 该元素为返回的分割点 Out[58]: array([0.999, 1.5 , 2. ])
-
qcut:
qcut 和 cut 几乎没有差别,只是把 bins 参数变成的 q 参数, qcut 中的 q 是指 quantile 。这里的 q 为整数 n 时,指按照 n 等分位数把数据分箱,还可以传入浮点列表指代相应的分位数分割点:
s = df.Weight pd.qcut(s,q=3).head() Out[60]: 0 (33.999, 48.0] 1 (55.0, 89.0] 2 (55.0, 89.0] 3 (33.999, 48.0] 4 (55.0, 89.0] Name: Weight, dtype: category Categories (3, interval[float64]): [(33.999, 48.0] < (48.0, 55.0] < (55.0, 89.0]] pd.qcut(s,q=[0,0.2,0.8,1]).head() Out[61]: 0 (44.0, 69.4] 1 (69.4, 89.0] 2 (69.4, 89.0] 3 (33.999, 44.0] 4 (69.4, 89.0] Name: Weight, dtype: category Categories (3, interval[float64]): [(33.999, 44.0] < (44.0, 69.4] < (69.4, 89.0]]
2.一般区间的构造
-
pd.Interval:
对于某一个具体的区间而言,其具备三个要素:
-
左端点
-
右端点
-
开闭状态,其中开闭状态可以指定 right, left, both, neither 中的一类:
my_interval = pd.Interval(0,1,'right') my_interval Out[63]: Interval(0, 1, closed='right')
其属性包含了mid, length, right, left, closed ,分别表示中点、长度、右端点、左端点和开闭状态。
使用 in 可以判断元素是否属于区间:
0.5 in my_interval Out[64]: True
使用 overlaps 可以判断两个区间是否有交集:
my_interval_2 = pd.Interval(0.5,1.5,'left') my_interval.overlaps(my_interval_2) Out[66]: True
-
-
pd.IntervalIndex 对象有四类方法生成,分别是:
-
from_breaks的功能类似于 cut 或 qcut 函数,只不过后两个是通过计算得到的风格点,而前者是直接传入自定义的分割点:
pd.IntervalIndex.from_breaks([1,3,6,10],closed='both') Out[67]: IntervalIndex([[1, 3], [3, 6], [6, 10]], closed='both', dtype='interval[int64]')
-
from_arrays是分别传入左端点和右端点的列表,适用于有交集并且知道起点和终点的情况:
pd.IntervalIndex.from_arrays(left = [1,3,6,10], ...: right = [5,4,9,11], ...: closed = 'neither') Out[68]: IntervalIndex([(1, 5), (3, 4), (6, 9), (10, 11)], closed='neither', dtype='interval[int64]')
-
from_tuples传入的是起点和终点元组构成的列表:
pd.IntervalIndex.from_tuples([(1,5),(3,4),(6,9),(10,11)],closed='neither') Out[69]: IntervalIndex([(1, 5), (3, 4), (6, 9), (10, 11)], closed='neither', dtype='interval[int64]') pd.interval_range(start=1,end=5,periods=8) Out[70]: IntervalIndex([(1.0, 1.5], (1.5, 2.0], (2.0, 2.5], (2.5, 3.0], (3.0, 3.5], (3.5, 4.0], (4.0, 4.5], (4.5, 5.0]], closed='right', dtype='interval[float64]')
-
一个等差的区间序列由起点、终点、区间个数和区间长度决定interval_range中的 start, end, periods, freq 参数就对应了这四个量,从而就能构造出相应的区间:
pd.interval_range(start=1,end=5,periods=8) Out[70]: IntervalIndex([(1.0, 1.5], (1.5, 2.0], (2.0, 2.5], (2.5, 3.0], (3.0, 3.5], (3.5, 4.0], (4.0, 4.5], (4.5, 5.0]], closed='right', dtype='interval[float64]') pd.interval_range(end=8,periods=8,freq=0.5) Out[71]: IntervalIndex([(4.0, 4.5], (4.5, 5.0], (5.0, 5.5], (5.5, 6.0], (6.0, 6.5], (6.5, 7.0], (7.0, 7.5], (7.5, 8.0]], closed='right', dtype='interval[float64]')
-
-
除此之外,如果直接使用 pd.IntervalIndex([…], closed=…) ,把 Interval 类型的列表组成传入其中转为区间索引,那么所有的区间会被强制转为指定的 closed 类型,因为 pd.IntervalIndex 只允许存放同一种开闭区间的 Interval 对象:
pd.IntervalIndex([my_interval,my_interval_2],closed='left') Out[74]: IntervalIndex([[0.0, 1.0), [0.5, 1.5)], closed='left', dtype='interval[float64]')
3.区间的属性与方法
IntervalIndex 上也定义了一些有用的属性和方法。同时,如果想要具体利用 cut 或者 qcut 的结果进行分析,那么需要先将其转为该种索引类型:
id_interval = pd.IntervalIndex(pd.cut(s,3))
id_interval
Out[76]:
IntervalIndex([(33.945, 52.333], (52.333, 70.667], (70.667, 89.0], (33.945, 52.333], (70.667, 89.0] ... (33.945, 52.333], (33.945, 52.333], (33.945, 52.333], (70.667, 89.0], (33.945, 52.333]],
closed='right',
name='Weight',
dtype='interval[float64]')
与单个Interval类型相似,IntervalIndex有若干属性:
id_demo = id_interval[:5]
id_demo
Out[78]:
IntervalIndex([(33.945, 52.333], (52.333, 70.667], (70.667, 89.0], (33.945, 52.333], (70.667, 89.0]],
closed='right',
name='Weight',
dtype='interval[float64]')
-
left:左端点
id_demo.left Out[79]: Float64Index([33.945, 52.333, 70.667, 33.945, 70.667], dtype='float64')
-
right:右端点
id_demo.right Out[80]: Float64Index([52.333, 70.667, 89.0, 52.333, 89.0], dtype='float64')
-
mid:均值
id_demo.mid Out[81]: Float64Index([43.138999999999996, 61.5, 79.8335, 43.138999999999996, 79.8335], dtype='float64')
-
length:区间长度
id_demo.length Out[82]: Float64Index([18.387999999999998, 18.334000000000003, 18.333, 18.387999999999998, 18.333], dtype='float64')
IntervalIndex 还有两个常用方法,包括 contains 和 overlaps ,分别指逐个判断每个区间是否包含某元素,以及是否和一个 pd.Interval 对象有交集:
id_demo.contains(4)
Out[83]: array([False, False, False, False, False])
id_demo.overlaps(pd.Interval(40,60))
Out[84]: array([ True, True, False, True, False])