Pandas vs Spark:獲取指定列的N種方式
導讀
本篇繼續(xù)Pandas與Spark常用操作對比系列,針對常用到的獲取指定列的多種實現(xiàn)做以對比。
注:此處的Pandas特指DataFrame數(shù)據(jù)結(jié)構,Spark特指spark.sql下的DataFrame數(shù)據(jù)結(jié)構。

無論是pandas的DataFrame還是spark.sql的DataFrame,獲取指定一列是一種很常見的需求場景,獲取指定列之后可以用于提取原數(shù)據(jù)的子集,也可以根據(jù)該列衍生其他列。在兩個計算框架下,都支持了多種實現(xiàn)獲取指定列的方式,但具體實現(xiàn)還是有一定區(qū)別的。
在pd.DataFrame數(shù)據(jù)結(jié)構中,提供了多種獲取單列的方式。由于Pandas中提供了兩種核心的數(shù)據(jù)結(jié)構:DataFrame和Series,其中DataFrame的任意一行和任意一列都是一個Series,所以某種意義上講DataFrame可以看做是Series的容器或集合。因此,如果從DataFrame中單獨取一列,那么得到的將是一個Series(當然,也可以將該列提取為一個只有單列的DataFrame,但本文仍以提取單列得到Series為例)。
首先生成一個普通的DataFrame為例:

對于如上DataFrame,需要提取其中的A列,則常用的方法有如下4種:
df.A:即應用屬性提取符"."的方式,但要求該列名稱符合一般變量名命名規(guī)范,包括不能以數(shù)字開頭,不能包含空格等特殊字符;
df['A']:即以方括號加列名的形式提取,這種方式容易理解,因為一個DataFrame本質(zhì)上可以理解為Python中的一個特殊字典,其中每個列名是key,每一列的數(shù)據(jù)為value(注:這個特殊的字典允許列名重復),該種形式對列名無任何要求。當方括號內(nèi)用一個列名組成的列表時,則意味著提取結(jié)果是一個DataFrame子集;
df.loc[:, 'A']:即通過定位符loc來提取,其中逗號前面用于定位目標行,此處用:即表示對行不限定;逗號后面用于定位目標列,此處用單個列名即表示提取單列,提取結(jié)果為該列對應的Series,若是用一個列名組成的列表,則表示提取多列得到一個DataFrame子集;
df.iloc[:, 0]:即通過索引定位符iloc實現(xiàn),與loc類似,只不過iloc中傳入的為整數(shù)索引形式,且索引從0開始;仍與loc類似,此處傳入單個索引整數(shù),若傳入多個索引組成的列表,則仍然提取得到一個DataFrame子集。
上述4種方法的對應示例如下:

注:以上方法僅示例提取單列得到一個Series結(jié)果。
spark.sql中也提供了名為DataFrame的核心數(shù)據(jù)抽象,其與Pandas中DataFrame有很多相近之處,但也有許多不同,典型區(qū)別包括:Spark中的DataFrame每一列的類型為Column、行為Row,而Pandas中的DataFrame則無論是行還是列,都是一個Series;Spark中DataFrame有列名,但沒有行索引,而Pandas中則既有列名也有行索引;Spark中DataFrame僅可作整行或者整列的計算,而Pandas中的DataFrame則可以執(zhí)行各種粒度的計算,包括元素級、行列級乃至整個DataFrame級別。當然,本文不過多對二者的區(qū)別做以介紹,而僅枚舉常用的提取特定列的方法。

scala spark構建一個示例DataFrame數(shù)據(jù)
對于如上DataFrame,仍然提取A列對應的DataFrame子集,常用方法如下:
df.select("A"):即直接用select算子+列名實現(xiàn);
df.select(df("A")):即通過圓括號提取符得到DataFrame中的單列Column對象,而后再用select算子得到相應的DataFrame;
df.select(col("A")):即首先通過col函數(shù)得到DataFrame中的單列Column對象,而后再用select算子得到相應的DataFrame。注意,這里的col函數(shù)需要首先從org.apache.spark.sql.functions中導入;
df.select($"A"):即通過美元符$+列名字符串隱式轉(zhuǎn)換為Column類型,而后再用select算子得到相應DataFrame,這里$"A"等價于col("A")。注意,能用$隱式轉(zhuǎn)換的前提是執(zhí)行隱式轉(zhuǎn)換導入:import spark.implicits._;
df.select('A):與用美元符$隱式轉(zhuǎn)換類似,也可用單側(cè)單引號實現(xiàn)隱式轉(zhuǎn)換,實質(zhì)上也是得到一個Column類型,即'A等價于col("A"),當然也需要首先執(zhí)行隱式轉(zhuǎn)換導入;
df.select(expr("A")):仍然是用一個函數(shù)expr+列名提取該列,這里expr執(zhí)行了類SQL的功能,可以接受一個該列的表達式執(zhí)行類SQL計算,例如此處僅用于提取A列,則直接賦予列名作為參數(shù)即可;
df.selectExpr("A"):對于上述select+expr的組合,spark.sql中提供了更為簡潔的替代形式,即selectExpr,可直接接受類SQL的表達式字符串,自然也可完成單列的提取,相當于是對上一種實現(xiàn)方式的精簡形式。
以上7種實現(xiàn)方式的示例如下:

本文分別列舉了Pandas和Spark.sql中DataFrame數(shù)據(jù)結(jié)構提取特定列的多種實現(xiàn),其中Pandas中DataFrame提取一列既可用于得到單列的Series對象,也可用于得到一個只有單列的DataFrame子集,常用的方法有4種;而Spark中提取特定一列,雖然也可得到單列的Column對象,但更多的還是應用select或selectExpr將1個或多個Column對象封裝成一個DataFrame,常用的方法多達7種,在這方面似乎靈活性相較于Pandas中DataFrame而言具有更為明顯的優(yōu)越性。但還是那個觀點,框架本身是本無高下優(yōu)劣之分,只有熟練靈活運用方顯高效。
最后,公布前期送書中獎讀者:根據(jù)當時截圖所示的3名近期分享最多的讀者中,中獎讀者如下:請該讀者于24小時內(nèi)通過公眾號后臺聯(lián)系小編,過時無效。


相關閱讀:
