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

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

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

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

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

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


相關(guān)閱讀:
