Pandas系列(十六)- 你須要學會的騷操做

pandas有一種功能很是強大的方法,它就是accessor,能夠將它理解爲一種屬性接口,經過它能夠得到額外的方法。其實這樣說仍是很籠統,下面咱們經過代碼和實例來理解一下。python

pd.Series._accessors
Out[93]: {'cat', 'dt', 'str'}

對於Series數據結構使用_accessors方法,咱們獲得了3個對象:cat,str,dtgit

  • .cat:用於分類數據(Categorical data)正則表達式

  • .str:用於字符數據(String Object data)api

  • .dt:用於時間數據(datetime-like data)數據結構

下面咱們依次看一下這三個對象是如何使用的。app

1、str對象的使用

  •  Series數據類型:str字符串
# 定義一個Series序列
addr = pd.Series([
    'Washington, D.C. 20003',
    'Brooklyn, NY 11211-1755',
    'Omaha, NE 68154',
    'Pittsburgh, PA 15211'
]) 
addr.str.upper()
Out[95]: 
0     WASHINGTON, D.C. 20003
1    BROOKLYN, NY 11211-1755
2            OMAHA, NE 68154
3       PITTSBURGH, PA 15211
dtype: object
addr.str.count(r'\d')
Out[96]: 
0    5
1    9
2    5
3    5
dtype: int64

關於以上str對象的2個方法說明:學習

  • Series.str.upper:將Series中全部字符串變爲大寫;spa

  • Series.str.count:對Series中全部字符串的個數進行計數;code

其實不難發現,該用法的使用與Python中字符串的操做很類似。沒錯,在pandas中你同樣能夠這樣簡單的操做,而不一樣的是你操做的是一整列的字符串數據。仍然基於以上數據集,再看它的另外一個操做:component

regex = (r'(?P<city>[A-Za-z ]+), '      # 一個或更多字母
         r'(?P<state>[A-Z]{2}) '        # 兩個大寫字母
         r'(?P<zip>\d{5}(?:-\d{4})?)')  # 可選的4個延伸數字
addr.str.replace('.', '').str.extract(regex)
Out[98]: 
         city state         zip
0  Washington    DC       20003
1    Brooklyn    NY  11211-1755
2       Omaha    NE       68154
3  Pittsburgh    PA       15211

關於以上str對象的2個方法說明:

  • Series.str.replace:將Series中指定字符串替換;

  • Series.str.extract:經過正則表達式提取字符串中的數據信息;

這個用法就有點複雜了,由於很明顯看到,這是一個鏈式的用法。經過replace將 " . " 替換爲"",即爲空,緊接着又使用了3個正則表達式(分別對應city,state,zip)經過extract對數據進行了提取,並由原來的Series數據結構變爲了DataFrame數據結構。 

固然,除了以上用法外,經常使用的屬性和方法還有.rstrip,.contains,split等,咱們經過下面代碼查看一下str屬性的完整列表:

[i for i in dir(pd.Series.str) if not i.startswith('_')]
Out[99]: 
['capitalize',
 'cat',
 'center',
 'contains',
 'count',
 'decode',
 'encode',
 'endswith',
 'extract',
 'extractall',
 'find',
 'findall',
 'get',
 'get_dummies',
 'index',
 'isalnum',
 'isalpha',
 'isdecimal',
 'isdigit',
 'islower',
 'isnumeric',
 'isspace',
 'istitle',
 'isupper',
 'join',
 'len',
 'ljust',
 'lower',
 'lstrip',
 'match',
 'normalize',
 'pad',
 'partition',
 'repeat',
 'replace',
 'rfind',
 'rindex',
 'rjust',
 'rpartition',
 'rsplit',
 'rstrip',
 'slice',
 'slice_replace',
 'split',
 'startswith',
 'strip',
 'swapcase',
 'title',
 'translate',
 'upper',
 'wrap',
 'zfill']

屬性有不少,對於具體的用法,若是感興趣能夠本身進行摸索練習。

2、dt對象的使用

  • Series數據類型:datetime

由於數據須要datetime類型,因此下面使用pandas的date_range()生成了一組日期datetime演示如何進行dt對象操做。

daterng = pd.Series(pd.date_range('2017', periods=9, freq='Q'))
daterng
Out[101]: 
0   2017-03-31
1   2017-06-30
2   2017-09-30
3   2017-12-31
4   2018-03-31
5   2018-06-30
6   2018-09-30
7   2018-12-31
8   2019-03-31
dtype: datetime64[ns]
daterng.dt.day_name()
Out[102]: 
0      Friday
1      Friday
2    Saturday
3      Sunday
4    Saturday
5    Saturday
6      Sunday
7      Monday
8      Sunday
dtype: object
# 查看下半年
daterng[daterng.dt.quarter > 2]
Out[104]: 
2   2017-09-30
3   2017-12-31
6   2018-09-30
7   2018-12-31
dtype: datetime64[ns]
daterng[daterng.dt.is_year_end]
Out[105]: 
3   2017-12-31
7   2018-12-31
dtype: datetime64[ns]

以上關於dt的3種方法說明:

  • Series.dt.day_name():從日期判斷出所處星期數;

  • Series.dt.quarter:從日期判斷所處季節;

  • Series.dt.is_year_end:從日期判斷是否處在年末;

其它方法也都是基於datetime的一些變換,並經過變換來查看具體微觀或者宏觀日期。

[i for i in dir(pd.Series.dt) if not i.startswith('_')]
Out[106]: 
['ceil',
 'components',
 'date',
 'day',
 'day_name',
 'dayofweek',
 'dayofyear',
 'days',
 'days_in_month',
 'daysinmonth',
 'floor',
 'freq',
 'hour',
 'is_leap_year',
 'is_month_end',
 'is_month_start',
 'is_quarter_end',
 'is_quarter_start',
 'is_year_end',
 'is_year_start',
 'microsecond',
 'microseconds',
 'minute',
 'month',
 'month_name',
 'nanosecond',
 'nanoseconds',
 'normalize',
 'quarter',
 'round',
 'second',
 'seconds',
 'strftime',
 'time',
 'to_period',
 'to_pydatetime',
 'to_pytimedelta',
 'total_seconds',
 'tz',
 'tz_convert',
 'tz_localize',
 'week',
 'weekday',
 'weekday_name',
 'weekofyear',
 'year']

3、cat對象的使用

  • Series數據類型:Category

在說cat對象的使用前,先說一下Category這個數據類型,它的做用很強大。雖然咱們沒有常常性的在內存中運行上g的數據,可是咱們也總會遇到執行幾行代碼會等待好久的狀況。使用Category數據的一個好處就是:能夠很好的節省在時間和空間的消耗。下面咱們經過幾個實例來學習一下。

 

colors = pd.Series([
    'periwinkle',
    'mint green',
    'burnt orange',
    'periwinkle',
    'burnt orange',
    'rose',
    'rose',
    'mint green',
    'rose',
    'navy'
])
import sys
colors.apply(sys.getsizeof)
Out[109]: 
0    59
1    59
2    61
3    59
4    61
5    53
6    53
7    59
8    53
9    53
dtype: int64

上面咱們經過使用sys.getsizeof來顯示內存佔用的狀況,數字表明字節數。

還有另外一種計算內容佔用的方法:memory_usage(),後面會使用。

如今咱們將上面colors的不重複值映射爲一組整數,而後再看一下佔用的內存。

mapper = {v: k for k, v in enumerate(colors.unique())}
mapper
Out[111]: {'periwinkle': 0, 'mint green': 1, 'burnt orange': 2, 'rose': 3, 'navy': 4}
as_int = colors.map(mapper)
as_int
Out[113]: 
0    0
1    1
2    2
3    0
4    2
5    3
6    3
7    1
8    3
9    4
dtype: int64
as_int.apply(sys.getsizeof)
Out[114]: 
0    24
1    28
2    28
3    24
4    28
5    28
6    28
7    28
8    28
9    28
dtype: int64
  • 注:對於以上的整數值映射也可使用更簡單的pd.factorize()方法代替。

咱們發現上面所佔用的內存是使用object類型時的一半。其實,這種狀況就相似於Category data類型內部的原理。

內存佔用區別:Categorical所佔用的內存與Categorical分類的數量和數據的長度成正比,相反,object所佔用的內存則是一個常數乘以數據的長度。 

下面是object內存使用和category內存使用的狀況對比。

colors.memory_usage(index=False, deep=True)
Out[115]: 650
colors.astype('category').memory_usage(index=False, deep=True)
Out[116]: 495

上面結果是使用object和Category兩種狀況下內存的佔用狀況。咱們發現效果並無咱們想象中的那麼好。可是注意Category內存是成比例的,若是數據集的數據量很大,但不重複分類(unique)值不多的狀況下,那麼Category的內存佔用能夠節省達到10倍以上,好比下面數據量增大的狀況:

manycolors = colors.repeat(10)
len(manycolors) / manycolors.nunique() 
Out[118]: 20.0
manycolors.memory_usage(index=False, deep=True)
Out[119]: 6500
manycolors.astype('category').memory_usage(index=False, deep=True)
Out[120]: 585

能夠看到,在數據量增長10倍之後,使用Category所佔內容節省了10倍以上。

除了佔用內存節省外,另外一個額外的好處是計算效率有了很大的提高。由於對於Category類型的Series,str字符的操做發生在.cat.categories的非重複值上,而並不是原Series上的全部元素上。也就是說對於每一個非重複值都只作一次操做,而後再向與非重複值同類的值映射過去。

對於Category的數據類型,可使用accessor的cat對象,以及相應的屬性和方法來操做Category數據。

ccolors = colors.astype('category')
ccolors.cat.categories
Out[122]: Index(['burnt orange', 'mint green', 'navy', 'periwinkle', 'rose'], dtype='object')
ccolors.unique()
Out[123]: 
[periwinkle, mint green, burnt orange, rose, navy]
Categories (5, object): [periwinkle, mint green, burnt orange, rose, navy]

實際上,對於開始的整數類型映射,咱們能夠先經過reorder_categories進行從新排序,而後再使用cat.codes來實現對整數的映射,來達到一樣的效果。

ccolors.cat.reorder_categories(mapper).cat.codes
Out[124]: 
0    0
1    1
2    2
3    0
4    2
5    3
6    3
7    1
8    3
9    4
dtype: int8

dtype類型是Numpy的int8(-127~128)。能夠看出以上只須要一個單字節就能夠在內存中包含全部的值。咱們開始的作法默認使用了int64類型,然而經過pandas的使用能夠很智能的將Category數據類型變爲最小的類型。

讓咱們來看一下cat還有什麼其它的屬性和方法可使用。下面cat的這些屬性基本都是關於查看和操做Category數據類型的。

 

[i for i in dir(ccolors.cat) if not i.startswith('_')]
Out[125]: 
['add_categories',
 'as_ordered',
 'as_unordered',
 'categories',
 'codes',
 'ordered',
 'remove_categories',
 'remove_unused_categories',
 'rename_categories',
 'reorder_categories',
 'set_categories']

可是Category數據的使用不是很靈活。例如,插入一個以前沒有的值,首先須要將這個值添加到.categories的容器中,而後再添加值。

ccolors.iloc[5] = 'a new color'
# ...
ValueError: Cannot setitem on a Categorical with a new category, set the categories first
ccolors = ccolors.cat.add_categories(['a new color'])
ccolors.iloc[5] = 'a new color'  

若是你想設置值或重塑數據,而非進行新的運算操做,那麼Category類型不是那麼有用。

相關文章
相關標籤/搜索