pandas有些功能很逆天,但卻鮮為人知,本篇給大家盤點一下。
一、ACCESSOR
pandas有一種功能非常強大的方法,它就是accessor,可以將它理解為一種屬性接口,通過它可以獲得額外的方法。其實這樣說還是很籠統,下面我們通過代碼和實例來理解一下。
<code>>>> pd.Series._accessors
{'cat', 'str', 'dt'}/<code>
對於Series數據結構使用_accessors方法,可以得到了3個對象:cat,str,dt。
- .cat:用於分類數據(Categorical data)
- .str:用於字符數據(String Object data)
- .dt:用於時間數據(datetime-like data)
下面我們依次看一下這三個對象是如何使用的。
str對象的使用
Series數據類型:str字符串
<code># 定義一個Series序列
>>> addr = pd.Series([
... 'Washington, D.C. 20003',
... 'Brooklyn, NY 11211-1755',
... 'Omaha, NE 68154',
... 'Pittsburgh, PA 15211'
... ])
>>> addr.str.upper()
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')
0 5
1 9
2 5
3 5
dtype: int64/<code>
關於以上str對象的2個方法說明:
- Series.str.upper:將Series中所有字符串變為大寫
- Series.str.count:對Series中所有字符串的個數進行計數
其實不難發現,該用法的使用與Python中字符串的操作很相似。沒錯,在pandas中你一樣可以這樣簡單的操作,而不同的是你操作的是一整列的字符串數據。仍然基於以上數據集,再看它的另一個操作:
<code>>>> regex = (r'(?P<city>[A-Za-z ]+), ' # 一個或更多字母
... r'(?P<state>[A-Z]{2}) ' # 兩個大寫字母
... r'(?P\\d{5}(?:-\\d{4})?)') # 可選的4個延伸數字 /<state>/<city>/<code>
...
>>> addr.str.replace('.', '').str.extract(regex)
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屬性的完整列表:
<code>>>> [i for i in dir(pd.Series.str) if not i.startswith('_')]
['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']/<code>
屬性有很多,對於具體的用法,如果感興趣可以自己進行摸索練習。
dt對象的使用
Series數據類型:datetime
因為數據需要datetime類型,所以下面使用pandas的date_range()生成了一組日期datetime演示如何進行dt對象操作。
<code>>>> daterng = pd.Series(pd.date_range('2017', periods=9, freq='Q'))
>>> daterng
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()
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]
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]
3 2017-12-31
7 2018-12-31
dtype: datetime64[ns]/<code>
以上關於dt的3種方法說明:
- Series.dt.day_name():從日期判斷出所處星期數
- Series.dt.quarter:從日期判斷所處季節
- Series.dt.is_year_end:從日期判斷是否處在年底
其它方法也都是基於datetime的一些變換,並通過變換來查看具體微觀或者宏觀日期。
cat對象的使用
Series數據類型:Category
在說cat對象的使用前,先說一下Category這個數據類型,它的作用很強大。雖然我們沒有經常性的在內存中運行上g的數據,但是我們也總會遇到執行幾行代碼會等待很久的情況。使用Category數據的一個好處就是:可以很好的節省在時間和空間的消耗。下面我們通過幾個實例來學習一下。
<code>>>> colors = pd.Series([
... 'periwinkle',
... 'mint green',
... 'burnt orange',
... 'periwinkle',
... 'burnt orange',
... 'rose',
... 'rose',
... 'mint green',
... 'rose',
... 'navy'
... ])
...
>>> import sys
>>> colors.apply(sys.getsizeof)
0 59
1 59
2 61
3 59
4 61
5 53
6 53
7 59
8 53
9 53
dtype: int64/<code>
上面我們通過使用sys.getsizeof來顯示內存佔用的情況,數字代表字節數。還有另一種計算內容佔用的方法:memory_usage(),後面會使用。
現在我們將上面colors的不重複值映射為一組整數,然後再看一下佔用的內存。
<code>>>> mapper = {v: k for k, v in enumerate(colors.unique())}
>>> mapper
{'periwinkle': 0, 'mint green': 1, 'burnt orange': 2, 'rose': 3, 'navy': 4}
>>> as_int = colors.map(mapper)
>>> as_int
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)
0 24
1 28
2 28
3 24
4 28
5 28
6 28
7 28
8 28
9 28
dtype: int64/<code>
注:對於以上的整數值映射也可以使用更簡單的pd.factorize()方法代替。
我們發現上面所佔用的內存是使用object類型時的一半。其實,這種情況就類似於Category data類型內部的原理。
內存佔用區別:Categorical所佔用的內存與Categorical分類的數量和數據的長度成正比,相反,object所佔用的內存則是一個常數乘以數據的長度。
下面是object內存使用和category內存使用的情況對比。
<code>>>> colors.memory_usage(index=False, deep=True)
650
>>> colors.astype('category').memory_usage(index=False, deep=True)
495/<code>
上面結果是使用object和Category兩種情況下內存的佔用情況。我們發現效果並沒有我們想象中的那麼好。但是注意Category內存是成比例的,如果數據集的數據量很大,但不重複分類(unique)值很少的情況下,那麼Category的內存佔用可以節省達到10倍以上,比如下面數據量增大的情況:
<code>>>> manycolors = colors.repeat(10)
>>> len(manycolors) / manycolors.nunique()
20.0
>>> manycolors.memory_usage(index=False, deep=True)
6500
>>> manycolors.astype('category').memory_usage(index=False, deep=True)
585/<code>
可以看到,在數據量增加10倍以後,使用Category所佔內容節省了10倍以上。
除了佔用內存節省外,另一個額外的好處是計算效率有了很大的提升。因為對於Category類型的Series,str字符的操作發生在.cat.categories的非重複值上,而並非原Series上的所有元素上。也就是說對於每個非重複值都只做一次操作,然後再向與非重複值同類的值映射過去。
對於Category的數據類型,可以使用accessor的cat對象,以及相應的屬性和方法來操作Category數據。
<code>>>> ccolors = colors.astype('category')
>>> ccolors.cat.categories
Index(['burnt orange', 'mint green', 'navy', 'periwinkle', 'rose'], dtype='object')/<code>
實際上,對於開始的整數類型映射,可以先通過reorder_categories進行重新排序,然後再使用cat.codes來實現對整數的映射,來達到同樣的效果。
<code>>>> ccolors.cat.reorder_categories(mapper).cat.codes
0 0
1 1
2 2
3 0
4 2
5 3
6 3
7 1
8 3
9 4
dtype: int8/<code>
dtype類型是Numpy的int8(-127~128)。可以看出以上只需要一個單字節就可以在內存中包含所有的值。我們開始的做法默認使用了int64類型,然而通過pandas的使用可以很智能的將Category數據類型變為最小的類型。
讓我們來看一下cat還有什麼其它的屬性和方法可以使用。下面cat的這些屬性基本都是關於查看和操作Category數據類型的。
<code>>>> [i for i in dir(ccolors.cat) if not i.startswith('_')]
['add_categories',
'as_ordered',
'as_unordered',
'categories',
'codes',
'ordered',
'remove_categories',
'remove_unused_categories',
'rename_categories',
'reorder_categories',
'set_categories']/<code>
但是Category數據的使用不是很靈活。例如,插入一個之前沒有的值,首先需要將這個值添加到.categories的容器中,然後再添加值。
<code>>>> 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' /<code>
如果你想設置值或重塑數據,而非進行新的運算操作,那麼Category類型不是那麼有用。
二、從clipboard剪切板載入數據
當我們的數據存在excel表裡,或者其它的IDE編輯器中的時候,我們想要通過pandas載入數據。我們通常的做法是先保存再載入,其實這樣做起來十分繁瑣。一個簡單的方法就是使用pd.read_clipboard() 直接從電腦的剪切板緩存區中提取數據。
這樣我們就可以直接將結構數據轉變為DataFrame或者Series了。excel表中數據是這樣的:
在純文本文件中,比如txt文件,是這樣的:
<code>a b c d
0 1 inf 1/1/00
2 7.389056099 N/A 5-Jan-13
4 54.59815003 nan 7/24/18
6 403.4287935 None NaT/<code>
將上面excel或者txt中的數據選中然後複製,然後使用pandas的read_clipboard()即可完成到DataFrame的轉換。parse_dates參數設置為 "d",可以自動識別日期,並調整為xxxx-xx-xx的格式。
<code>>>> df = pd.read_clipboard(na_values=[None], parse_dates=['d'])
>>> df
a b c d
0 0 1.0000 inf 2000-01-01
1 2 7.3891 NaN 2013-01-05
2 4 54.5982 NaN 2018-07-24
3 6 403.4288 NaN NaT
>>> df.dtypes
a int64
b float64
c float64
d datetime64[ns]
dtype: object/<code>
三、將pandas對象轉換為“壓縮”格式
在pandas中,我們可以直接將objects打包成為 gzip, bz2, zip, or xz 等壓縮格式,而不必將沒壓縮的文件放在內存中然後進行轉化。來看一個例子如何使用:
<code>>>> abalone = pd.read_csv(url, usecols=[0, 1, 2, 3, 4, 8], names=cols)
>>> abalone
sex length diam height weight rings
0 M 0.455 0.365 0.095 0.5140 15
1 M 0.350 0.265 0.090 0.2255 7
2 F 0.530 0.420 0.135 0.6770 9
3 M 0.440 0.365 0.125 0.5160 10
4 I 0.330 0.255 0.080 0.2050 7
5 I 0.425 0.300 0.095 0.3515 8
6 F 0.530 0.415 0.150 0.7775 20
... .. ... ... ... ... ...
4170 M 0.550 0.430 0.130 0.8395 10
4171 M 0.560 0.430 0.155 0.8675 8
4172 F 0.565 0.450 0.165 0.8870 11
4173 M 0.590 0.440 0.135 0.9660 10
4174 M 0.600 0.475 0.205 1.1760 9
4175 F 0.625 0.485 0.150 1.0945 10
4176 M 0.710 0.555 0.195 1.9485 12/<code>
導入文件,讀取並存為abalone(DataFrame結構)。當我們要存為壓縮的時候,簡單的使用 to_json() 即可輕鬆完成轉化過程。下面通過設置相應參數將abalone存為了.gz格式的壓縮文件。
<code>abalone.to_json('df.json.gz', orient='records',
lines=True, compression='gzip')/<code>
如果我們想知道儲存壓縮文件的大小,可以通過內置模塊os.path,使用getsize方法來查看文件的字節數。下面是兩種格式儲存文件的大小對比。
<code>>>> import os.path
>>> abalone.to_json('df.json', orient='records', lines=True)
>>> os.path.getsize('df.json') / os.path.getsize('df.json.gz')
11.603035760226396/<code>
四、使用"測試模塊"製作偽數據
在pandas中,有一個測試模塊可以幫助我們生成半真實(偽數據),並進行測試,它就是util.testing。下面同我們通過一個簡單的例子看一下如何生成數據測試:
<code>>>> import pandas.util.testing as tm
>>> tm.N, tm.K = 15, 3 # 默認的行和列
>>> import numpy as np
>>> np.random.seed(444)
>>> tm.makeTimeDataFrame(freq='M').head()
A B C
2000-01-31 0.3574 -0.8804 0.2669
2000-02-29 0.3775 0.1526 -0.4803
2000-03-31 1.3823 0.2503 0.3008
2000-04-30 1.1755 0.0785 -0.1791
2000-05-31 -0.9393 -0.9039 1.1837
>>> tm.makeDataFrame().head()
A B C
nTLGGTiRHF -0.6228 0.6459 0.1251
WPBRn9jtsR -0.3187 -0.8091 1.1501
7B3wWfvuDA -1.9872 -1.0795 0.2987
yJ0BTjehH1 0.8802 0.7403 -1.2154
0luaYUYvy1 -0.9320 1.2912 -0.2907/<code>
上面簡單的使用了
makeTimeDataFrame 和 makeDataFrame 分別生成了一組時間數據和DataFrame的數據。但這只是其中的兩個用法,關於testing中的方法有大概30多個,如果你想全部瞭解,可以通過查看dir獲得:
<code>>>> [i for i in dir(tm) if i.startswith('make')]
['makeBoolIndex',
'makeCategoricalIndex',
'makeCustomDataframe',
'makeCustomIndex',
# ...,
'makeTimeSeries',
'makeTimedeltaIndex',
'makeUIntIndex',
'makeUnicodeIndex']/<code>
五、從列項中創建DatetimeIndex
也許我們有的時候會遇到這樣的情形(為了說明這種情情況,我使用了product進行交叉迭代的創建了一組關於時間的數據):
<code>>>> from itertools import product
>>> datecols = ['year', 'month', 'day']
>>> df = pd.DataFrame(list(product([2017, 2016], [1, 2], [1, 2, 3])),
... columns=datecols)
>>> df['data'] = np.random.randn(len(df))
>>> df
year month day data
0 2017 1 1 -0.0767
1 2017 1 2 -1.2798
2 2017 1 3 0.4032
3 2017 2 1 1.2377
4 2017 2 2 -0.2060
5 2017 2 3 0.6187
6 2016 1 1 2.3786
7 2016 1 2 -0.4730
8 2016 1 3 -2.1505
9 2016 2 1 -0.6340
10 2016 2 2 0.7964
11 2016 2 3 0.0005/<code>
明顯看到,列項中有year,month,day,它們分別在各個列中,而並非是一個完整日期。那麼如何從這些列中將它們組合在一起並設置為新的index呢?
通過to_datetime的使用,我們就可以直接將年月日組合為一個完整的日期,然後賦給索引。代碼如下:
<code>>>> df.index = pd.to_datetime(df[datecols])
>>> df.head()
year month day data
2017-01-01 2017 1 1 -0.0767
2017-01-02 2017 1 2 -1.2798
2017-01-03 2017 1 3 0.4032
2017-02-01 2017 2 1 1.2377
2017-02-02 2017 2 2 -0.2060/<code>
當然,你可以選擇將原有的年月日列移除,只保留data數據列,然後squeeze轉換為Series結構。
<code>>>> df = df.drop(datecols, axis=1).squeeze()
>>> df.head()
2017-01-01 -0.0767
2017-01-02 -1.2798
2017-01-03 0.4032
2017-02-01 1.2377
2017-02-02 -0.2060
Name: data, dtype: float64
>>> df.index.dtype_str
'datetime64[ns]/<code>
最後小編為大家準備了Python學習的電子書籍及視頻教程
獲取方式:轉發此文,關注並私信小編“學習”即可免費領取哦
書籍部分截圖:
視頻教程部分截圖:
獲取方式:轉發此文,關注並私信小編“學習”即可免費領取哦
閱讀更多 宇文說編程 的文章