學(xué)習這門語言兩個月了,還是卡在了加減乘除這里...
因為業(yè)務(wù)需要(項目技術(shù)棧為 spark 2+ ),七八月份興沖沖從學(xué)校圖書館借了書,學(xué)了 scala + spark ,還寫了不少博文,其中有幾篇被拿來發(fā)推送:Scala,一門「特立獨行」的語言!、【疑惑】如何從 Spark 的 DataFrame 中取出具體某一行? ...
但實際操作起來,還是遇到不少問題。
收獲經(jīng)驗有二:
- 看書(尤其國內(nèi)教材)理解理解概念還行,但是對于實際操作沒啥大用
- 接觸一門新的編程語言,首先應(yīng)該閱讀大量優(yōu)秀的案例代碼,還有理解清楚數(shù)據(jù)類型
舉個例子,我昨天上午一直被這個糾纏著:請你給 spark 中 dataframe 的某一列數(shù) 取為。
按理說不難吧。要是 python 的 pandas 就直接上了:
- # pandas
- df['sig_x'] = df['x'].apply(lambda x: 1 / (1 - np.exp(-x)))
但是 spark 不行。
spark 中,新建一列使用的函數(shù)是 withColumn ,首先傳入函數(shù)名,接下來傳入一個 col 對象。
這個 col 對象就有講究了,雖然我今天看來還是比較直觀好理解的,但是昨天可就在里面周旋了好一陣子。
首先,如果我想使用列 x ,我不可以直接 "x" ,因為這是一個字符串,我需要調(diào)用隱式轉(zhuǎn)換的函數(shù) $ ,而這個 $ 實際上在 spark.implicits._ 中。
值得注意的是, spark 是你的 SparkSession 實例。
上述內(nèi)容不清楚,則需要花一陣子找資料。
- import spark.implicits._
- val df_new = df.withColumn("x_new", $"x")
上述代碼構(gòu)造了一個新 df_new 對象,其中有 x_new 列與 x 列,兩列數(shù)值完全一致。
其次,我的運算函數(shù)在哪里找呢?
答案是 org.apache.spark.sql.functions ,因為是 col 對象,其可能沒有重載與常數(shù)數(shù)據(jù)類型的 + - * / 運算符,因此,如果我們 1 - $"x" 可能會報錯:因為 #"x" 是 col ,而 1 只是一個 Int 。
我們要做的就是把 1 變成一個 col :苦苦查閱資料后,我找到了 lit 方法,也是在 org.apache.spark.sql.functions 中。最終的方案如下。
- import spark.implicits._
- import org.apache.spark.sql.functions.{fit, exp, negate}
- val df_result = df_raw_result
- .withColumn("x_sig",
- lit(1.0) / (lit(1.0) + exp(negate($"x")))
- )
其實,實際的代碼比上面的還要復(fù)雜,因為 "x" 列里面其實是一個 vector 對象,我直接 $"x"(0) 無法取出 $"x" 列中的向量的第一個元素,最后查到用 udf 可以實現(xiàn)列的函數(shù)。
- import spark.implicits._
- import org.apache.spark.sql.functions.{fit, exp, negate, udf}
- // 取向量中的第一個元素
- val getItem = udf((v: org.apache.spark.ml.linalg.DenseVector, i: Int) => v(i))
- val df_result = df_raw_result
- .withColumn("x_sig",
- lit(1.0) / (lit(1.0) + exp(negate(getItem($"x", lit(0)))))
- )
python 和 scala ?
看起來,似乎 python 下的操作更加簡潔優(yōu)雅,但我更喜歡用 scala 書寫這種級別的項目。
原因很簡單, scala 對于類型的嚴格要求已經(jīng)其從函數(shù)式編程那里借鑒來的思想,讓代碼寫得太爽了。大部分問題,編譯期就能發(fā)現(xiàn),而且配合上 IDEA 的自動補全,真的很舒服。
目前為止,還沒有弄懂 udf 代表著什么,基礎(chǔ)語法與框架思想這里還是有待查缺補漏。