本文為您介紹DataFrame API中的列運算。
列運算
from odps.df import DataFrame
iris = DataFrame(o.get_table('pyodps_iris'))
lens = DataFrame(o.get_table('pyodps_ml_100k_lens'))
為一個Sequence加上一個常量或執行sin函數時,這些操作將作用於Sequence中的每個元素。
NULL相關(isnull,notnull,fillna)
DataFrame API提供了幾個和NULL相關的內建函數,例如
isnull
用於判斷某欄位是否為NULL,notnull
用於判斷某欄位是否為非NULL,fillna
用於將NULL填充為您指定的值。
>>> iris.sepallength.isnull().head(5)
sepallength
0 False
1 False
2 False
3 False
4 False
邏輯判斷(ifelse,switch)
ifelse
作用於BOOLEAN類型的欄位,當條件成立時,返回第0個參數,否則返回第1個參數。
>>> (iris.sepallength > 5).ifelse('gt5', 'lte5').rename('cmp5').head(5)
cmp5
0 gt5
1 lte5
2 lte5
3 lte5
4 lte5
switch
用於多條件判斷的情況。
>>> iris.sepallength.switch(4.9, 'eq4.9', 5.0, 'eq5.0', default='noeq').rename('equalness').head(5)
equalness
0 noeq
1 eq4.9
2 noeq
3 noeq
4 eq5.0
>>> from odps.df import switch
>>> switch(iris.sepallength == 4.9, 'eq4.9', iris.sepallength == 5.0, 'eq5.0', default='noeq').rename('equalness').head(5)
equalness
0 noeq
1 eq4.9
2 noeq
3 noeq
4 eq5.0
PyODPS 0.7.8以上版本支援根據條件修改資料集某一列的一部分值,寫法如下。
>>> iris[iris.sepallength > 5, 'cmp5'] = 'gt5'
>>> iris[iris.sepallength <= 5, 'cmp5'] = 'lte5'
>>> iris.head(5)
cmp5
0 gt5
1 lte5
2 lte5
3 lte5
4 lte5
數學運算
對於數字類型的欄位,支援加法(+)、減法(-)、乘法(*)和除法(/)等操作,也支援log、sin等數學計算。
>>> (iris.sepallength * 10).log().head(5)
sepallength
0 3.931826
1 3.891820
2 3.850148
3 3.828641
4 3.912023
>>> fields = [iris.sepallength,
>>> (iris.sepallength / 2).rename('sepallength除以2'),
>>> (iris.sepallength ** 2).rename('sepallength的平方')]
>>> iris[fields].head(5)
sepallength sepallength除以2 sepallength的平方
0 5.1 2.55 26.01
1 4.9 2.45 24.01
2 4.7 2.35 22.09
3 4.6 2.30 21.16
4 5.0 2.50 25.00
算術運算支援如下操作。
算術操作 | 說明 |
abs | 絕對值 |
sqrt | 平方根 |
sin | 無 |
sinh | 無 |
cos | 無 |
cosh | 無 |
tan | 無 |
tanh | 無 |
arccos | 無 |
arccosh | 無 |
arcsin | 無 |
arcsinh | 無 |
arctan | 無 |
arctanh | 無 |
exp | 指數函數 |
expm1 | 指數減1 |
log | 傳入參數表示底是幾。 |
log2 | 無 |
log10 | 無 |
log1p | log(1+x) |
radians | 給定角度計算弧度。 |
degrees | 給定弧度計算角度。 |
ceil | 不小於輸入值的最小整數。 |
floor | 向下取整,返回比輸入值小的整數值。 |
trunc | 將輸入值截取到指定小數點位置。 |
Sequence也支援自身與其它Sequence或Scalar的比較。
>>> (iris.sepallength < 5).head(5)
sepallength
0 False
1 True
2 True
3 True
4 False
雖然DataFrame API不支援連續操作,例如
3 <= iris.sepallength <= 5
,但是between
函數可以用於判斷iris.sepallength
是否在某個區間。
>>> (iris.sepallength.between(3, 5)).head(5)
sepallength
0 False
1 True
2 True
3 True
4 True
預設情況下,
between
包含兩邊的區間,如果計算開區間,則需要設定inclusive=False
。
>>> (iris.sepallength.between(3, 5, inclusive=False)).head(5)
sepallength
0 False
1 True
2 True
3 True
4 False
STRING相關操作
DataFrame API提供了一系列針對STRING類型的Sequence或者Scalar的操作。
>>> fields = [
>>> iris.name.upper().rename('upper_name'),
>>> iris.name.extract('Iris(.*)', group=1)
>>> ]
>>> iris[fields].head(5)
upper_name name
0 IRIS-SETOSA -setosa
1 IRIS-SETOSA -setosa
2 IRIS-SETOSA -setosa
3 IRIS-SETOSA -setosa
4 IRIS-SETOSA -setosa
STRING的相關操作如下。
STRING操作 | 說明 |
capitalize | 無 |
contains | 包含某個字串,如果regex 參數為True,則是包含某個Regex,預設為True。 |
count | 指定字串出現的次數。 |
endswith | 以某個字串結尾。 |
startswith | 以某個字串開頭。 |
extract | 抽取出某個Regex,如果group不指定,則返回滿足整個pattern的子串;否則,返回第幾個group。 |
find | 返回第一次出現的子串位置,若不存在則返回-1。 |
rfind | 從右尋找返回子串第一次出現的位置,不存在則返回-1。 |
replace | 將某個pattern的子串全部替換成另一個子串, n參數若指定,則替換n次。 |
get | 返回某個位置上的字串。 |
len | 返回字串的長度。 |
ljust | 若未達到指定的width 的長度,則在右側填充fillchar 指定的字串(預設空格)。 |
rjust | 若未達到指定的width 的長度,則在左側填充fillchar 指定的字串(預設空格)。 |
lower | 變為全部小寫。 |
upper | 變為全部大寫。 |
lstrip | 在左側刪除空格(包括空行符)。 |
rstrip | 在右側刪除空格(包括空行符)。 |
strip | 在左右兩側刪除空格(包括空行符)。 |
split | 將字串按分隔字元拆分為若干個字串(返回 list<string>類型)。 |
pad | 在指定的位置(left,right或者both)用指定填充字元(用fillchar 指定,預設空格)來對齊。 |
repeat | 重複指定n次。 |
slice | 切片操作。 |
swapcase | 對調大小寫。 |
title | 同str.title 。 |
zfill | 長度沒達到指定width ,則左側填充0。 |
isalnum | 同str.isalnum 。 |
isalpha | 同str.isalpha 。 |
isdigit | 是否都是數字,同str.isdigit 。 |
isspace | 是否都是空格,同str.isspace 。 |
islower | 是否都是小寫,同str.islower 。 |
isupper | 是否都是大寫,同str.isupper 。 |
istitle | 同str.istitle 。 |
isnumeric | 同str.isnumeric 。 |
isdecimal | 同str.isdecimal 。 |
todict | 將字串按分隔字元拆分為一個Dict,傳入的兩個參數分別為專案分隔字元和Key-Value分隔字元(返回dict<string, string>類型)。 |
strptime |
格式化時間,時間格式和Python標準庫相同,詳情請參見 Python 時間格式化。 |
時間相關操作
對於DATETIME類型Sequence或者Scalar,可以調用時間相關的內建函數。
>>> df = lens[[lens.unix_timestamp.astype('datetime').rename('dt')]]
>>> df[df.dt,
>>> df.dt.year.rename('year'),
>>> df.dt.month.rename('month'),
>>> df.dt.day.rename('day'),
>>> df.dt.hour.rename('hour')].head(5)
dt year month day hour
0 1998-04-08 11:02:00 1998 4 8 11
1 1998-04-08 10:57:55 1998 4 8 10
2 1998-04-08 10:45:26 1998 4 8 10
3 1998-04-08 10:25:52 1998 4 8 10
4 1998-04-08 10:44:19 1998 4 8 10
與時間相關的屬性如下。
時間相關屬性 | 說明 |
year | 無 |
month | 無 |
day | 無 |
hour | 無 |
minute | 無 |
second | 無 |
weekofyear | 返回日期位於那一年的第幾周。周一作為一周的第一天。 |
weekday | 返回日期當前周的第幾天。 |
dayofweek | 返回日期當前周的第幾天。 |
strftime |
格式化時間,時間格式和Python 標準庫相同,詳情請參見 Python 時間格式化。 |
PyODPS也支援時間的加減操作,例如可以通過以下方法得到前3天的日期。兩個日期列相減得到相差的毫秒數。
>>> df
a b
0 2016-12-06 16:43:12.460001 2016-12-06 17:43:12.460018
1 2016-12-06 16:43:12.460012 2016-12-06 17:43:12.460021
2 2016-12-06 16:43:12.460015 2016-12-06 17:43:12.460022
>>> from odps.df import day
>>> df.a - day(3)
a
0 2016-12-03 16:43:12.460001
1 2016-12-03 16:43:12.460012
2 2016-12-03 16:43:12.460015
>>> (df.b - df.a).dtype
int64
>>> (df.b - df.a).rename('a')
a
0 3600000
1 3600000
2 3600000
支援的時間類型如下表所示。
屬性 | 說明 |
year | 無 |
month | 無 |
day | 無 |
hour | 無 |
minute | 無 |
second | 無 |
millisecond | 無 |
集合類型相關操作
PyODPS支援的集合類型有List和Dict。這兩個類型都可以使用下標擷取集合中的某個專案。len
方法用於獲得集合的大小。
同時,兩種集合均有explode
方法,用於展開集合中的內容。對於List,explode
預設返回一列,當傳入參數pos
時, 將返回兩列,其中一列為值在數組中的編號(類似Python的enumerate
函數)。對於Dict,explode
會返回兩列, 分別表示keys及values。explode
中也可以傳入列名,作為最後產生的列。
樣本如下。
>>> df
id a b
0 1 [a1, b1] {'a2': 0, 'b2': 1, 'c2': 2}
1 2 [c1] {'d2': 3, 'e2': 4}
>>> df[df.id, df.a[0], df.b['b2']]
id a b
0 1 a1 1
1 2 c1 NaN
>>> df[df.id, df.a.len(), df.b.len()]
id a b
0 1 2 3
1 2 1 2
>>> df.a.explode()
a
0 a1
1 b1
2 c1
>>> df.a.explode(pos=True)
a_pos a
0 0 a1
1 1 b1
2 0 c1
>>> # 指定列名。
>>> df.a.explode(['pos', 'value'], pos=True)
pos value
0 0 a1
1 1 b1
2 0 c1
>>> df.b.explode()
b_key b_value
0 a2 0
1 b2 1
2 c2 2
3 d2 3
4 e2 4
>>> # 指定列名。
>>> df.b.explode(['key', 'value'])
key value
0 a2 0
1 b2 1
2 c2 2
3 d2 3
4 e2 4
explode
也可以和並列多行輸出結合,以將原有列和explode
的結果相結合,樣本如下。
>>> df[df.id, df.a.explode()]
id a
0 1 a1
1 1 b1
2 2 c1
>>> df[df.id, df.a.explode(), df.b.explode()]
id a b_key b_value
0 1 a1 a2 0
1 1 a1 b2 1
2 1 a1 c2 2
3 1 b1 a2 0
4 1 b1 b2 1
5 1 b1 c2 2
6 2 c1 d2 3
7 2 c1 e2 4
除了下標
len
和explode
兩個共有方法以外,List還支援下列方法。
List 操作 | 說明 |
contains(v) | 列表是否包含某個元素。 |
sort | 返回排序後的列表(傳回值為List)。 |
Dict還支援下列方法。
dict操作 | 說明 |
keys | 擷取Dict keys(傳回值為List)。 |
values | 擷取Dict values(傳回值為List)。 |
其它元素操作(isin,notin,cut)
isin
用於判斷Sequence裡的元素是否在某個集合元素裡,notin
反之。
>>> iris.sepallength.isin([4.9, 5.1]).rename('sepallength').head(5)
sepallength
0 True
1 True
2 False
3 False
4 False
cut
提供離散化的操作,可以將Sequence的資料拆成幾個區段。
>>> iris.sepallength.cut(range(6), labels=['0-1', '1-2', '2-3', '3-4', '4-5']).rename('sepallength_cut').head(5)
sepallength_cut
0 None
1 4-5
2 4-5
3 4-5
4 4-5
include_under
和include_over
可以分別包括向下和向上的區間。
>>> labels = ['0-1', '1-2', '2-3', '3-4', '4-5', '5-']
>>> iris.sepallength.cut(range(6), labels=labels, include_over=True).rename('sepallength_cut').head(5)
sepallength_cut
0 5-
1 4-5
2 4-5
3 4-5
4 4-5
調用MaxCompute內建或者已定義函數
如果您需要調用MaxCompute上的內建或者已定義函數來產生列,您可以使用
func
介面,該介面預設函數傳回值為STRING,可以用rtype
參數指定傳回值。
>>> from odps.df import func
>>>
>>> iris[iris.name, func.rand(rtype='float').rename('rand')][:4]
>>> iris[iris.name, func.rand(10, rtype='float').rename('rand')][:4]
>>> # 調用ODPS上定義的UDF,列名無法確定時需要手動指定。
>>> iris[iris.name, func.your_udf(iris.sepalwidth, iris.sepallength, rtype='float').rename('new_col')]
>>> # 從其它Project調用UDF,也可通過name參數指定列名。
>>> iris[iris.name, func.your_udf(iris.sepalwidth, iris.sepallength, rtype='float', project='udf_project', name='new_col')]
說明 Pandas後端不支援執行帶有
func
的運算式。