DataFrame中所有二維資料集上的操作都屬於CollectionExpr,可視為一張MaxCompute表或一張電子錶單,DataFrame對象也是CollectionExpr的特例。CollectionExpr中包含針對二維資料集的列操作、篩選、變換等大量操作。
前提條件
您需要提前完成以下步驟,用於操作本文中的樣本:
準備樣本表pyodps_iris,詳情請參見Dataframe資料處理。
建立DataFrame,詳情請參見從MaxCompute表建立DataFrame。
擷取類型
dtypes
可以用來擷取CollectionExpr中所有列的類型,dtypes
返回的是Schema類型 ,程式碼範例如下。
print(iris.dtypes)
返回結果:
odps.Schema {
sepallength float64
sepalwidth float64
petallength float64
petalwidth float64
name string
}
列選擇和增刪
列選擇
如果您需要從一個CollectionExpr中選取部分列,產生新的資料集,可以使用expr[columns]
文法,程式碼範例如下。
print(iris['name', 'sepallength'].head(5))
返回結果:
name sepallength
0 Iris-setosa 4.9
1 Iris-setosa 4.7
2 Iris-setosa 4.6
3 Iris-setosa 5.0
4 Iris-setosa 5.4
如果只需要選取一列,需要在Columns後加上逗號或者顯示標記為列表,例如iris[iris.sepallength, ]
或iris[[iris.sepallength]]
,否則返回的將是一個Sequence對象,而不是Collection。
列刪除
如果您需要在新的資料集中排除已有資料集的某些列,可使用
exclude
方法,程式碼範例如下。print(iris.exclude('sepallength', 'petallength')[:5].head(5))
返回結果:
sepalwidth petalwidth name 0 3.0 0.2 Iris-setosa 1 3.2 0.2 Iris-setosa 2 3.1 0.2 Iris-setosa 3 3.6 0.2 Iris-setosa 4 3.9 0.4 Iris-setosa
PyODPS 0.7.2版(及以上版本)支援另一種寫法,即在資料集上直接排除相應的列。程式碼範例如下。
del iris['sepallength'] del iris['petallength'] print(iris[:5].head(5))
返回結果:
sepalwidth petalwidth name 0 3.0 0.2 Iris-setosa 1 3.2 0.2 Iris-setosa 2 3.1 0.2 Iris-setosa 3 3.6 0.2 Iris-setosa 4 3.9 0.4 Iris-setosa
列增加
如果您需要在已有Collection中添加某一列變換的結果,可以使用
expr[expr, new_sequence]
文法, 新增的列會作為新Collection的一部分。樣本:將
iris
中的sepalwidth
列加一後重新命名為sepalwidthplus1
並追加到資料集末尾,形成新的資料集。 代碼如下:print(iris[iris, (iris.sepalwidth + 1).rename('sepalwidthplus1')].head(5))
返回結果:
sepallength sepalwidth petallength petalwidth name \ 0 4.9 3.0 1.4 0.2 Iris-setosa 1 4.7 3.2 1.3 0.2 Iris-setosa 2 4.6 3.1 1.5 0.2 Iris-setosa 3 5.0 3.6 1.4 0.2 Iris-setosa 4 5.4 3.9 1.7 0.4 Iris-setosa sepalwidthplus1 0 4.0 1 4.2 2 4.1 3 4.6 4 4.9
使用
expr[expr, new_sequence]
文法需要注意,變換後的列名與原列名可能相同。如果需要與原Collection合并, 請將該列重新命名。PyODPS 0.7.2版(及以上版本)支援直接在當前資料集中追加,程式碼範例如下。iris['sepalwidthplus1'] = iris.sepalwidth + 1 print(iris.head(5))
返回結果:
sepallength sepalwidth petallength petalwidth name \ 0 4.9 3.0 1.4 0.2 Iris-setosa 1 4.7 3.2 1.3 0.2 Iris-setosa 2 4.6 3.1 1.5 0.2 Iris-setosa 3 5.0 3.6 1.4 0.2 Iris-setosa 4 5.4 3.9 1.7 0.4 Iris-setosa sepalwidthplus1 0 4.0 1 4.2 2 4.1 3 4.6 4 4.9
同時增刪列
您可以先將原列通過
exclude
方法進行排除,再將變換後的新列併入,而不必擔心重名。程式碼範例如下。print(iris[iris.exclude('sepalwidth'), iris.sepalwidth * 2].head(5))
返回結果:
sepallength petallength petalwidth name sepalwidth 0 4.9 1.4 0.2 Iris-setosa 6.0 1 4.7 1.3 0.2 Iris-setosa 6.4 2 4.6 1.5 0.2 Iris-setosa 6.2 3 5.0 1.4 0.2 Iris-setosa 7.2 4 5.4 1.7 0.4 Iris-setosa 7.8
PyODPS 0.7.2版(及以上版本)支援直接在當前資料集上覆蓋的操作。程式碼範例如下。
iris['sepalwidth'] = iris.sepalwidth * 2 print(iris.head(5))
返回結果:
sepallength sepalwidth petallength petalwidth name 0 4.9 6.0 1.4 0.2 Iris-setosa 1 4.7 6.4 1.3 0.2 Iris-setosa 2 4.6 6.2 1.5 0.2 Iris-setosa 3 5.0 7.2 1.4 0.2 Iris-setosa 4 5.4 7.8 1.7 0.4 Iris-setosa
以建立新Collection的方式實現增刪列的另一種方法是調用
select
,將需要選擇的列作為參數輸入。如果您需要重新命名,可以使用keyword
參數輸入,並將新的列名作為參數名。程式碼範例如下。print(iris.select('name', sepalwidthminus1=iris.sepalwidth - 1).head(5))
返回結果:
name sepalwidthminus1 0 Iris-setosa 2.0 1 Iris-setosa 2.2 2 Iris-setosa 2.1 3 Iris-setosa 2.6 4 Iris-setosa 2.9
您也可以傳入一個Lambda運算式,它接收的參數為上一步的運算結果。在執行時,PyODPS會檢查這些Lambda運算式,傳入上一步產生的Collection並將其替換為正確的列。 程式碼範例如下。
print(iris['name', 'petallength'][[lambda x: x.name]].head(5))
返回結果:
name 0 Iris-setosa 1 Iris-setosa 2 Iris-setosa 3 Iris-setosa 4 Iris-setosa
PyODPS 0.7.2版(及以上版本)支援對資料進行條件賦值。 程式碼範例如下。
iris[iris.sepallength > 5.0, 'sepalwidth'] = iris.sepalwidth * 2 print(iris.head(5))
返回結果:
sepallength sepalwidth petallength petalwidth name 0 4.9 3.0 1.4 0.2 Iris-setosa 1 4.7 3.2 1.3 0.2 Iris-setosa 2 4.6 3.1 1.5 0.2 Iris-setosa 3 5.0 3.6 1.4 0.2 Iris-setosa 4 5.4 7.8 1.7 0.4 Iris-setosa
引入常數和隨機數
引入常數
DataFrame支援在Collection中追加一列常數。追加常數需要使用
Scalar
,引入時需要手動指定列名。 程式碼範例如下。from odps.df import Scalar print(iris[iris, Scalar(1).rename('id')][:5].head(5))
返回結果:
sepallength sepalwidth petallength petalwidth name id 0 4.9 3.0 1.4 0.2 Iris-setosa 1 1 4.7 3.2 1.3 0.2 Iris-setosa 1 2 4.6 3.1 1.5 0.2 Iris-setosa 1 3 5.0 3.6 1.4 0.2 Iris-setosa 1 4 5.4 3.9 1.7 0.4 Iris-setosa 1
如果需要指定一個空值列,您可以使用
NullScalar
,但需要提供欄位類型。 程式碼範例如下。from odps.df import NullScalar print(iris[iris, NullScalar('float').rename('fid')][:5].head(5))
返回結果:
sepallength sepalwidth petallength petalwidth name fid 0 4.9 3.0 1.4 0.2 Iris-setosa None 1 4.7 3.2 1.3 0.2 Iris-setosa None 2 4.6 3.1 1.5 0.2 Iris-setosa None 3 5.0 3.6 1.4 0.2 Iris-setosa None 4 5.4 3.9 1.7 0.4 Iris-setosa None
在PyODPS 0.7.12(及以上版本)中,引入了簡化寫法。 程式碼範例如下。
iris['id'] = 1 print(iris.head(5))
返回結果:
sepallength sepalwidth petallength petalwidth name id 0 4.9 3.0 1.4 0.2 Iris-setosa 1 1 4.7 3.2 1.3 0.2 Iris-setosa 1 2 4.6 3.1 1.5 0.2 Iris-setosa 1 3 5.0 3.6 1.4 0.2 Iris-setosa 1 4 5.4 3.9 1.7 0.4 Iris-setosa 1
需要注意的是,這種寫法無法自動識別空值的類型,所以在增加空值列時,仍然要使用如下代碼。
iris['null_col'] = NullScalar('float') print(iris.head(5))
返回結果:
sepallength sepalwidth petallength petalwidth name null_col 0 4.9 3.0 1.4 0.2 Iris-setosa None 1 4.7 3.2 1.3 0.2 Iris-setosa None 2 4.6 3.1 1.5 0.2 Iris-setosa None 3 5.0 3.6 1.4 0.2 Iris-setosa None 4 5.4 3.9 1.7 0.4 Iris-setosa None
引入隨機數
DataFrame也支援在Collection中增加一列隨機數列,該列類型為FLOAT,範圍為0~1,每行數值均不同。 追加隨機數列需要使用RandomScalar
,參數為隨機數種子,可省略。程式碼範例如下。
from odps.df import RandomScalar
iris[iris, RandomScalar().rename('rand_val')][:5]
返回結果:
sepallength sepalwidth petallength petalwidth name rand_val
0 4.9 3.0 1.4 0.2 Iris-setosa 0.000471
1 4.7 3.2 1.3 0.2 Iris-setosa 0.799520
2 4.6 3.1 1.5 0.2 Iris-setosa 0.834609
3 5.0 3.6 1.4 0.2 Iris-setosa 0.106921
4 5.4 3.9 1.7 0.4 Iris-setosa 0.763442
過濾資料
Collection提供了資料過濾的功能。支援使用與(&) 、或(|)、非(~)、filter
、Lambda運算式,及其他多種查詢方式對資料進行過濾。
樣本1:查詢
sepallength
大於5的資料。print(iris[iris.sepallength > 5].head(5))
返回結果:
sepallength sepalwidth petallength petalwidth name 0 5.4 3.9 1.7 0.4 Iris-setosa 1 5.4 3.7 1.5 0.2 Iris-setosa 2 5.8 4.0 1.2 0.2 Iris-setosa 3 5.7 4.4 1.5 0.4 Iris-setosa 4 5.4 3.9 1.3 0.4 Iris-setosa
樣本2:與(&)條件 。
print(iris[(iris.sepallength < 5) & (iris['petallength'] > 1.5)].head(5))
返回結果:
sepallength sepalwidth petallength petalwidth name 0 4.8 3.4 1.6 0.2 Iris-setosa 1 4.8 3.4 1.9 0.2 Iris-setosa 2 4.7 3.2 1.6 0.2 Iris-setosa 3 4.8 3.1 1.6 0.2 Iris-setosa 4 4.9 2.4 3.3 1.0 Iris-versicolor
樣本3:或(|)條件 。
print(iris[(iris.sepalwidth < 2.5) | (iris.sepalwidth > 4)].head(5))
返回結果:
sepallength sepalwidth petallength petalwidth name 0 5.7 4.4 1.5 0.4 Iris-setosa 1 5.2 4.1 1.5 0.1 Iris-setosa 2 5.5 4.2 1.4 0.2 Iris-setosa 3 4.5 2.3 1.3 0.3 Iris-setosa 4 5.5 2.3 4.0 1.3 Iris-versicolor
說明與和或條件必須使用
&
和|
,不能使用and
和or
。樣本4:非(~)條件 。
print(iris[~(iris.sepalwidth > 3)].head(5))
返回結果:
sepallength sepalwidth petallength petalwidth name 0 4.9 3.0 1.4 0.2 Iris-setosa 1 4.4 2.9 1.4 0.2 Iris-setosa 2 4.8 3.0 1.4 0.1 Iris-setosa 3 4.3 3.0 1.1 0.1 Iris-setosa 4 5.0 3.0 1.6 0.2 Iris-setosa
樣本5:顯式調用
filter
方法,提供多個與條件。print(iris.filter(iris.sepalwidth > 3.5, iris.sepalwidth < 4).head(5))
返回結果:
sepallength sepalwidth petallength petalwidth name 0 5.0 3.6 1.4 0.2 Iris-setosa 1 5.4 3.9 1.7 0.4 Iris-setosa 2 5.4 3.7 1.5 0.2 Iris-setosa 3 5.4 3.9 1.3 0.4 Iris-setosa 4 5.7 3.8 1.7 0.3 Iris-setosa
樣本6:對於連續的操作,使用Lambda運算式。
print(iris[iris.sepalwidth > 3.8]['name', lambda x: x.sepallength + 1].head(5))
返回結果:
name sepallength 0 Iris-setosa 6.4 1 Iris-setosa 6.8 2 Iris-setosa 6.7 3 Iris-setosa 6.4 4 Iris-setosa 6.2
樣本7:對於Collection,如果它包含一個列是BOOLEAN類型,則可以直接使用該列作為過濾條件。
# 查詢Schema print(df.dtypes) # 返回結果 odps.Schema { a boolean b int64 } # a列為boolean類型,執行過濾操作 print(df[df.a]) # 返回結果 a b 0 True 1 1 True 3
因此,對Collection取單個Sequence的操作時,只有BOOLEAN列是合法的,即可以對Collection進行以下過濾操作。
df[df.a, ] # 取列操作。 df[[df.a]] # 取列操作。 df.select(df.a) # 顯式取列。 df[df.a] # a列是boolean列,執行過濾操作。 df.a # 取單列。 df['a'] # 取單列。
樣本8:使用Pandas中的
query
方法,通過查詢語句做資料的篩選,在運算式中直接使用列名(如sepallength
)進行操作。在查詢語句中,&
和and
都表示與操作,|
和or
都表示或操作。print(iris.query("(sepallength < 5) and (petallength > 1.5)").head(5))
返回結果:
sepallength sepalwidth petallength petalwidth name 0 4.8 3.4 1.6 0.2 Iris-setosa 1 4.8 3.4 1.9 0.2 Iris-setosa 2 4.7 3.2 1.6 0.2 Iris-setosa 3 4.8 3.1 1.6 0.2 Iris-setosa 4 4.9 2.4 3.3 1.0 Iris-versicolor
當運算式中需要使用到本地變數時,需要在該變數前加一個
@
首碼。var = 4 print(iris.query("(sepalwidth < 2.5) | (sepalwidth > @var)").head(5))
返回結果:
sepallength sepalwidth petallength petalwidth name 0 5.7 4.4 1.5 0.4 Iris-setosa 1 5.2 4.1 1.5 0.1 Iris-setosa 2 5.5 4.2 1.4 0.2 Iris-setosa 3 4.5 2.3 1.3 0.3 Iris-setosa 4 5.5 2.3 4.0 1.3 Iris-versicolor
目前
query
支援的文法,如下表所示。文法
說明
name
沒有
@
首碼的都當作列名處理,有首碼的會擷取本地變數。operator
支援部分運算子:
+
、-
、*
、/
、//
、%
、**
、==
、!=
、<
、<=
、>
、>=
、in
、not in
。bool
與或非操作,其中
&
和and
表示與,|
和or
表示或。attribute
取對象屬性。
index, slice, subscript
切片操作。
並列多行輸出
對於LIST及MAP類型的列,
explode
方法會將該列轉換為多行輸出。您也可以使用apply
方法實現多行輸出。為了進行彙總等操作,常常需要將這些輸出和原表中的列合并。此時可以使用DataFrame提供的並列多行輸出功能, 寫法為將多行輸出函數產生的集合與原集合中的列名一起映射。 並列多行輸出的樣本如下。查詢樣本資料:
print(df)
返回結果:
id a b 0 1 [a1, b1] [a2, b2, c2] 1 2 [c1] [d2, e2]
樣本1:
print(df[df.id, df.a.explode(), df.b])
返回結果:
id a b 0 1 a1 [a2, b2, c2] 1 1 b1 [a2, b2, c2] 2 2 c1 [d2, e2]
樣本2:
print(df[df.id, df.a.explode(), df.b.explode()])
返回結果:
id a b 0 1 a1 a2 1 1 a1 b2 2 1 a1 c2 3 1 b1 a2 4 1 b1 b2 5 1 b1 c2 6 2 c1 d2 7 2 c1 e2
如果多行輸出方法對某個輸入不產生任何輸出,預設輸入行將不在最終結果中出現。如果需要在結果中出現該行,可以設定
keep_nulls=True
。此時,與該行並列的值將輸出為空白值。樣本如下。查詢樣本資料:
print(df)
返回結果:
id a 0 1 [a1, b1] 1 2 []
樣本1:
print(df[df.id, df.a.explode()])
返回結果:
id a 0 1 a1 1 1 b1
樣本2:
print(df[df.id, df.a.explode(keep_nulls=True)])
返回結果:
id a 0 1 a1 1 1 b1 2 2 None
使用
explode
方法實現並列多行輸出的樣本,請參見集合類型相關操作。
限制條數
輸出前三條資料。
print(iris[:3].execute())
返回結果:
sepallength sepalwidth petallength petalwidth name 0 4.9 3.0 1.4 0.2 Iris-setosa 1 4.7 3.2 1.3 0.2 Iris-setosa 2 4.6 3.1 1.5 0.2 Iris-setosa
目前對於MaxCompute SQL,後端切片不支援
start
和step
方法,但支援limit
方法。print(iris.limit(3).execute())
返回結果:
sepallength sepalwidth petallength petalwidth name 0 4.9 3.0 1.4 0.2 Iris-setosa 1 4.7 3.2 1.3 0.2 Iris-setosa 2 4.6 3.1 1.5 0.2 Iris-setosa
說明切片操作只能作用於Collection,不能作用於Sequence。