一辽故、cat對象
cat對象屬性
- 類別本身,通過Index類型存儲
- 是否有序, 通過的cat屬性訪問
s.cat.categories
s.cat.ordered
s.cat.codes #編號
類別的增刪改
s = s.cat.add_categories('Graduate') # 增加一個(gè)畢業(yè)生類別
s = s.cat.remove_categories('Freshman')
s = s.cat.set_categories(['Sophomore','PhD']) # 新類別為大二學(xué)生
和博士
s = s.cat.remove_unused_categories() # 移除了未出現(xiàn)的博士生類別
s = s.cat.rename_categories({'Sophomore':'本科二年級學(xué)生'})
二、有序分類
序的建立
- 通過
s.cat.as_ordered()
可以將類別轉(zhuǎn)化為有序曙旭,有序類別和無序類別可以通過as_unordered
和reorder_categories
互相轉(zhuǎn)化
s = df.Grade.astype('category')
s = s.cat.reorder_categories(['Freshman', 'Sophomore', 'Junior', 'Senior'],ordered=True)
s.head()
s.cat.as_unordered().head()
排序和比較
- 有序的類別可以使用
sort_index
和sort_values
進(jìn)行排序 - 也可以是使用比較運(yùn)算符進(jìn)行比較,主義在使用大小比較時(shí)比較的對象必須存在category中晶府,不然無法比較
三桂躏、區(qū)間類別
cut和qcut
-
cut
:可以指定分割區(qū)間的數(shù)量或者通過list指定端點(diǎn)值
pd.cut(s, bins=2, right=False)
pd.cut(s, bins=[-np.infty, 1.2, 1.8, 2.2, np.infty])
-
qcut
:可以指定分位等分的數(shù)量或者通過list指定端點(diǎn)值(分位數(shù))
pd.qcut(s, q=3)
pd.qcut(s, q=[0,0.2,0.8,1])
二者皆可使用labels指定區(qū)間名稱
區(qū)間的構(gòu)造
- 通過pd.Interval構(gòu)造, 指定左右端點(diǎn)和閉合開閉狀態(tài)
my_interval = pd.Interval(0, 1, 'right')
- 通過pd.IntervalIndex構(gòu)造
- 從cut或者qcut的結(jié)果轉(zhuǎn)換
- from_breaks
- from_arrays
- from_tuples
- interval_range
id_interval = pd.IntervalIndex(pd.cut(s, 3))
pd.IntervalIndex.from_breaks([1,3,6,10], closed='both')
pd.IntervalIndex.from_arrays(left = [1,3,6,10], right = [5,4,9,11], closed = 'neither')
pd.IntervalIndex.from_tuples([(1,5),(3,4),(6,9),(10,11)], closed='neither')
pd.interval_range(start=1,end=5,periods=8)
【練一練】
無論是interval_range
還是下一章時(shí)間序列中的date_range
都是給定了等差序列中四要素中的三個(gè),從而確定整個(gè)序列川陆。請回顧等差數(shù)列中的首項(xiàng)剂习、末項(xiàng)、項(xiàng)數(shù)和公差的聯(lián)系较沪,寫出interval_range
中四個(gè)參數(shù)之間的恒等關(guān)系鳞绕。
(end - start) / freq == periodes
區(qū)間的屬性與方法
- overlaps:判斷是否有交集
id_demo.overlaps(pd.Interval(40,60))
- contains: 判斷區(qū)間是否含有某個(gè)元素
id_demo.contains(4)
- 屬性:
- left
- right
- mid
- length
id_demo.left
id_demo.right
id_demo.mid
id_demo.length
四、練習(xí)
Ex1:統(tǒng)計(jì)未出現(xiàn)的類別
在第五章中介紹了crosstab
函數(shù)尸曼,在默認(rèn)參數(shù)下它能夠?qū)蓚€(gè)列的組合出現(xiàn)的頻數(shù)進(jìn)行統(tǒng)計(jì)匯總:
df = pd.DataFrame({'A':['a','b','c','a'], 'B':['cat','cat','dog','cat']})
pd.crosstab(df.A, df.B)
但事實(shí)上有些列存儲的是分類變量们何,列中并不一定包含所有的類別,此時(shí)如果想要對這些未出現(xiàn)的類別在crosstab
結(jié)果中也進(jìn)行匯總控轿,則可以指定dropna
參數(shù)為False
:
請實(shí)現(xiàn)一個(gè)帶有dropna
參數(shù)的my_crosstab
函數(shù)來完成上面的功能冤竹。
構(gòu)造s1與s2的dataframe, 將s1.name作為index茬射,索引出相關(guān)的行鹦蠕,然后使用
==
計(jì)算與s2.name相等的元素的個(gè)數(shù)
def my_crosstab(s1, s2, dropna=True):
columns = s2.cat.categories[s2.cat.categories.isin(s2)]
table = pd.concat([s1,s2], axis=1).set_index(s1.name)
if dropna:
_columns = columns
else:
_columns = s2.cat.categories
ret = pd.DataFrame(index=index, columns=_columns, data=np.zeros((len(index), len(_columns))))
res = res.rename_axis(index=s1.name, columns=s2.name).astype('int')
for idx in index:
content = table.loc[idx]
for c in columns:
ret.loc[idx, c] = (content == c).values.sum()
return ret
my_crosstab(s1, s2, dropna=False)
Ex2:鉆石數(shù)據(jù)集
現(xiàn)有一份關(guān)于鉆石的數(shù)據(jù)集,其中carat, cut, clarity, price
分別表示克拉重量躲株、切割質(zhì)量片部、純凈度和價(jià)格,樣例如下:
df = pd.read_csv('../data/diamonds.csv')
df.head(3)
- 分別對
df.cut
在object
類型和category
類型下使用nunique
函數(shù)霜定,并比較它們的性能档悠。 - 鉆石的切割質(zhì)量可以分為五個(gè)等級,由次到好分別是
Fair, Good, Very Good, Premium, Ideal
望浩,純凈度有八個(gè)等級辖所,由次到好分別是I1, SI2, SI1, VS2, VS1, VVS2, VVS1, IF
,請對切割質(zhì)量按照由好到次的順序排序磨德,相同切割質(zhì)量的鉆石缘回,按照純凈度進(jìn)行由次到好的排序吆视。 - 分別采用兩種不同的方法,把
cut, clarity
這兩列按照由好到次的順序酥宴,映射到從0到n-1的整數(shù)啦吧,其中n表示類別的個(gè)數(shù)。 - 對每克拉的價(jià)格按照分別按照分位數(shù)(q=[0.2, 0.4, 0.6, 0.8])與[1000, 3500, 5500, 18000]割點(diǎn)進(jìn)行分箱得到五個(gè)類別
Very Low, Low, Mid, High, Very High
拙寡,并把按這兩種分箱方法得到的category
序列依次添加到原表中授滓。 - 第4問中按照整數(shù)分箱得到的序列中,是否出現(xiàn)了所有的類別肆糕?如果存在沒有出現(xiàn)的類別請把該類別刪除般堆。
- 對第4問中按照分位數(shù)分箱得到的序列,求每個(gè)樣本對應(yīng)所在區(qū)間的左右端點(diǎn)值和長度诚啃。
根據(jù)結(jié)果可知淮摔, category類型速度略快
# 性能測量
%timeit -n 100 df.cut.nunique()
cat = df.cut.astype('category')
%timeit -n 100 cat.nunique()
轉(zhuǎn)換為category類型后使用reorder_categories轉(zhuǎn)換成有序類型排序即可
df.cut = df.cut.astype('category').cat.reorder_categories(['Fair', 'Good', 'Very Good', 'Premium', 'Ideal'])
df.clarity = df.clarity.astype('category').cat.reorder_categories(['I1', 'SI2', 'SI1', 'VS2', 'VS1', 'VVS2', 'VVS1', 'IF'])
df.sort_values(['cut', 'clarity'], ascending=[False, True]).head()
利用cat.code或者replace
df.cut = df.cut.cat.reorder_categories(df.cut.cat.categories[::-1])
df.clarity = df.clarity.cat.reorder_categories(df.clarity.cat.categories[::-1])
df.cut = df.cut.cat.codes # 方法一:利用cat.codes
clarity_cat = df.clarity.cat.categories
df.clarity = df.clarity.replace(dict(zip(clarity_cat, np.arange(len(clarity_cat))))) # 方法二:使用replace映射
使用qcut和cut,注意的是對區(qū)間進(jìn)行補(bǔ)全始赎,使其正確地分為5個(gè)區(qū)間
pricePerCarat = df.price / df.carat
type1 = pd.qcut(pricePerCarat, q=[0, 0.2, 0.4, 0.6, 0.8, 1], labels=['Very Low', 'Low', 'Mid', 'High', 'Very High'])
type2 = pd.cut(pricePerCarat, bins=[0, 1000, 3500, 5500, 18000, np.inf], labels=['Very Low', 'Low', 'Mid', 'High', 'Very High'])
type1.name = 'type1'
type2.name = 'type2'
df = pd.concat([df, type1, type2], axis=1)
df.head()
通過唯一值的數(shù)目判斷所有的種類是否都出現(xiàn)了和橙,可以知道使用cut劃分區(qū)間的種類中少了
Very Low
和Very High
,使用remove_categories移除不存在的類別即可
print(df.type1.cat.categories.nunique() == df.type1.nunique())
print(df.type2.cat.categories.nunique() == df.type2.nunique())
cond = df.type2.cat.categories.isin(df.type2)
df.type2.cat.remove_categories(df.type2.cat.categories[~cond])
使用pd.IntervalIndex將分區(qū)結(jié)果轉(zhuǎn)換成區(qū)間之后調(diào)用相關(guān)屬性即可
interval = pd.IntervalIndex(pd.qcut(pricePerCarat, q=[0, 0.2, 0.4, 0.6, 0.8, 1]))
interval.right #右端點(diǎn)
interval.left #左端點(diǎn)
interval.length #區(qū)間長度