1.Spark SQL中的一個空值處理
背景是Dataframe轉(zhuǎn)為表之后使用Spark SQL,并需要過濾一些數(shù)值型空值。
Scala中的NaN值,一般可以看作是Float或Double類型,DF存儲為空字符串。
val a = Float.NaN
轉(zhuǎn)為表(createTempView)之后,DF中的NaN值會被記為“NaN”,該"NaN“值不會參與(Spark SQL的)count、avg等聚合運(yùn)算,如果需要對該值在Spark SQL中進(jìn)行識別或處理,可以在使用(相當(dāng)于把字符串賦了一個類型):
cast('NaN' as float)
進(jìn)一步的,Scala中的空值還有Nil(空列表)、null(無值無類型,一些文章建議少用)和Nothing(拋異常的時候用)等情況。
此外還有比較有特色Option(包含Some/None)等類型,其中None為”Empty“(isEmpty的結(jié)果),可以轉(zhuǎn)String等類型,理論上使用Option類型的安全性更好,但之前用的其他方式加的保護(hù),還沒機(jī)會深究。
2. Spark SQL中的列表
背景是在Spark SQL中做Group by 聚合,然后字段的聚合方法是把分組內(nèi)的數(shù)據(jù)寫入一個List或Array(作為一個字段)。實(shí)際Spark官網(wǎng)提供了不少SQL內(nèi)置函數(shù),摘錄幾個用到的:
- 最基本的方法是collect_list(col),之后字段形式為[1,2,3,4,……]這種,其他還可以參考官網(wǎng)中collect或array開頭的幾個函數(shù)。比如:
select col1, sort_array(array_remove(collect_list(col2)), cast('NaN' as float)) from table1 group by col1
該例子可以理解為按col1列聚合之后,將col2的內(nèi)容記錄為列表,然后移除列表中的NaN值,然后再排序。
- 實(shí)際工程中,上例實(shí)際還用了一個UDF函數(shù)做進(jìn)一步處理,因此排序和去除空值的工作,實(shí)際也可以利用UDF函數(shù)進(jìn)行,但簡單測試看了一下,貌似在SQL中處理和在UDF中處理的效率基本是一致的。
- Spark SQL中無論輸出的是List(collect_list結(jié)果)還是Array(sort_array結(jié)果),自定義函數(shù)的輸入條件都要用Seq,這個真是困惑了好久。
//定義udf的時候
def getmiddle(col: Seq[Float]):Float ={……}
/*使用的時候*/
select col1, getmiddle(collect_list(col2)) from table1 group by col1
3. Spark SQL中的結(jié)構(gòu)體+列表
在Spark SQL中使用結(jié)構(gòu)體,有struct和name_struct兩類,理論上后者應(yīng)該對應(yīng)scala的case class,但在SQL中利用聚合方式即時生成了一個復(fù)合型的數(shù)據(jù),類型大致為:array[float,string],把他包裝為array(struct)或array(name_struct)并傳到一個UDF函數(shù)中進(jìn)行進(jìn)一步處理,UDF的入口參數(shù)類型是seq[case class],此時一直報錯:
org.apache.spark.sql.catalyst.expressions.GenericRowWithSchema cannot be cast to myCaseClass
有文章說這種參數(shù)傳遞時,會丟棄schema,所以無法轉(zhuǎn)為case class,我沒找到原文,但目前看起來是這樣的。還嘗試了struct轉(zhuǎn)成元組等,也有問題,但懶得再細(xì)琢磨了。
后來實(shí)驗(yàn)了三種方法:
- 1,SQL這邊用struct或name_struct都行,然后UDF這邊用seq[ROW]類型(org.apache.spark.sql._),然后用row(0)、row(1)這種方式取值(any類型)即可。
- 2,SQL這邊加個to_json:array(to_json(name_struct)),然后UDF用字符串序列接收,再依次轉(zhuǎn)json處理。
- 3,直接用多個array傳參數(shù),代碼其實(shí)更簡潔,但我總害怕出現(xiàn)位置錯誤,比如一些空值等情況,導(dǎo)致兩個隊(duì)列中的數(shù)值關(guān)系錯位等(邏輯上感覺不會,但沒做過測試,怕沒想周全)
4. DF的map等操作的一些限制
嘗試在DF的map操作中使用math-case或if,一直出錯,但感覺語法應(yīng)該是對的,因?yàn)橄嗤Z法拿掉條件語句就可以運(yùn)行。不知道是不支持還是怎樣,這個一直沒解決。
df.map(x=>
{
if ……else……
})
此外在DF的map中使用另一個廣播變量DF(遍歷一個小表),一直會報錯,這個也沒解決。