天天看點

學習這門語言兩個月了,還是卡在了加減乘除這裡...

學習這門語言兩個月了,還是卡在了加減乘除這裡...

unsplash.com/@genessapana

因為業務需要(項目技術棧為

spark 2+

),七八月份興沖沖從學校圖書館借了書,學了

scala + spark

,還寫了不少博文,其中有幾篇被拿來發推送:Scala,一門「特立獨行」的語言!、【疑惑】如何從 Spark 的 DataFrame 中取出具體某一行? ...

但實際操作起來,還是遇到不少問題。

收獲經驗有二:

  • 看書(尤其國内教材)了解了解概念還行,但是對于實際操作沒啥大用
  • 接觸一門新的程式設計語言,首先應該閱讀大量優秀的案例代碼,還有了解清楚資料類型

舉個例子,我昨天上午一直被這個糾纏着:請你給

spark

dataframe

的某一列數

x_i

取為

sigmoid(x_i) = \frac{1}{1 - e^{-x_i}}

按理說不難吧。要是

python

pandas

就直接上了:

# pandas
df['sig_x'] = df['x'].apply(lambda x: 1 / (1 - np.exp(-x)))
           

複制

但是

spark

不行。

spark

中,建立一列使用的函數是

withColumn

,首先傳入函數名,接下來傳入一個 col 對象。

這個

col

對象就有講究了,雖然我今天看來還是比較直覺好了解的,但是昨天可就在裡面周旋了好一陣子。

首先,如果我想使用列 x ,我不可以直接 "x" ,因為這是一個字元串,我需要調用隐式轉換的函數

值得注意的是,

spark

是你的

SparkSession

執行個體。

上述内容不清楚,則需要花一陣子找資料。

import spark.implicits._
val df_new = df.withColumn("x_new", $"x")
           

複制

上述代碼構造了一個新

df_new

對象,其中有

x_new

列與

x

列,兩列數值完全一緻。

其次,我的運算函數在哪裡找呢?

答案是

org.apache.spark.sql.functions

,因為是

col

對象,其可能沒有重載與常數資料類型的

+

-

*

/

運算符,是以,如果我們

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")))
  )
           

複制

其實,實際的代碼比上面的還要複雜,因為 "x" 列裡面其實是一個 vector 對象,我直接

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

下的操作更加簡潔優雅,但我更喜歡用

scala

書寫這種級别的項目。

原因很簡單,

scala

對于類型的嚴格要求已經其從函數式程式設計那裡借鑒來的思想,讓代碼寫得太爽了。大部分問題,編譯期就能發現,而且配合上 IDEA 的自動補全,真的很舒服。

目前為止,還沒有弄懂

udf

代表着什麼,基礎文法與架構思想這裡還是有待查缺補漏。