モブプロな人たちのブログ

横浜で Web サービス開発しているエンジニアの日記です。Python 大好き Flask 大好き。たまに SpriteKit でゲーム開発も。

PySpark の DataFrame で Case-sensitive を有効にしたい!!

こんにちは、kaorr です。

一人アドベントカレンダー8日目です。

はじめに

先日構築した PySpark の検証環境、実は1つだけ問題があるのです。

なんと、Case-insensitive なんです!!

f:id:kaorr_mob:20171208180547j:plain

サンプルデータの作成と要件

これまでとだいたい同じですが、こんな感じです。

# createDataFrame でデータフレームを作成する
df = spark.createDataFrame([('yamada', None, 200), ('tanaka', 300, None), ('takeda', 400, 200), ('suzuki', None, None)], ['name', 'salary', 'SALARY'])

# データフレームの中身を見る
df.show()

# ↓こんな感じの出力があれば OK
# 
# +------+------+------+
# |  name|salary|SALARY|
# +------+------+------+
# |yamada|  null|   200|
# |tanaka|   300|  null|
# |takeda|   400|   200|
# |suzuki|  null|  null|
# +------+------+------+
  • 社員ごとの給与を出力したい

  • 給与パターン A(小文字) と給与パターン B(大文字) が存在する

  • A がある場合は A を出力する (B は無視)

  • A がなくて B がある場合は B を出力する

  • 両方ない場合は 0 を出力する

CASE 式の適用とデータの確認

# カラムの処理を良い感じにする col と、CASE 式用の when を import
from pyspark.sql.functions import col, when

# select で name と salary(CASE 式の結果) を指定
# when をメソッドチェーンで繋いでいく
df_result = df.select(col('name'), when(col('salary').isNotNull(), col('salary')).when(col('SALARY').isNotNull(), col('SALARY')).otherwise(0).alias('salary'))

# すると・・・
# ↓こんな感じのエラーが出力!!
# 大文字小文字区別しないから、どっちがどっちか分からないよ〜というエラー
# 
# AnalysisException: u"Reference 'salary' is ambiguous, could be: salary#1L, salary#2L.;"

# Case-sensitive を有効にする(魔法のコマンド)
spark.sql('set spark.sql.caseSensitive=true')

# 先ほどのクエリをもう一度実行、今度は成功!!
df_result = df.select(col('name'), when(col('salary').isNotNull(), col('salary')).when(col('SALARY').isNotNull(), col('SALARY')).otherwise(0).alias('salary'))

# df_result の中身を見る
df_result.show()

# ↓こんな感じの出力があれば OK
# 
# +------+------+
# |  name|salary|
# +------+------+
# |yamada|   200|
# |tanaka|   300|
# |takeda|   400|
# |suzuki|     0|
# +------+------+

おわりに

パッと見ると Python のレイヤーで動いてるように見えて Case-sensitive なのかと思いきや、裏で動いてる部分で Case-insensitive になっていたんですね。

エラーが出るとビックリするので、デフォルトで Case-sensitive にしておいて欲しいものですが・・・。

f:id:kaorr_mob:20171208181714j:plain