Databricks (Spark) にて dropduplicate や join にてエスケープ文字であるシングルクォートを指定した際のエラーへの対応方法

## 概要

Databricks (Spark) にて dropduplicate や join にてエスケープ文字であるシングルクォートを指定した際のエラーへの対応方法を共有します。日本語のカラム（例：`名前`）を Spark の SQL で扱う際にはシングルクォートを指定する必要があるのですが、 dropduplicate や join などを利用する際にシングルクォートを指定すると下記のようなエラーが発生します。

> AnalysisException: Cannot resolve column name "`名前`" among (名前, 数値).

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/120b20fd-b808-e0d9-801a-1bcd41930742.png)

> AnalysisException: [UNRESOLVED_USING_COLUMN_FOR_JOIN] USING column `名前` cannot be resolved on the left side of the join. The left-side columns: [`名前`, `数値`].

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/70e890b8-d3f0-e2b1-546b-614798df41eb.png)

対応方法としては、dropduplicate や join を実行する際には、シングルクォートを TRIM する方法です。

```python
col_list = ["`名前`"]
trimed_col_list = [col.lstrip("`").rstrip("`") for col in col_list]
df.dropDuplicates(trimed_col_list).display()
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/43fb9ffa-6b1e-6321-37e6-40a6997671e1.png)

2024/06/04 時点にて、`select`メソッドではエラーとならないが`dropDuplicates`メソッドでエラーとなる原因を把握できておりませんが、自分のコードを見返した際に上記のような TRIM 処理を実施している理由を思い返せないため、備忘録として記事にします。Github Copilot から下記のような回答がでましたが、明確な理由を追えておりません。

> Apache Sparkにおける`select`メソッドと`dropDuplicates`メソッドの動作の違いにより、この現象が起こります。
> 
> `select`メソッドでは、引数として与えられた文字列をそのままカラム名として解釈します。したがって、バッククオート(``)を含む文字列もカラム名として解釈されます。
> 
> 一方、`dropDuplicates`メソッドでは、引数として与えられた文字列をバッククオート()なしのカラム名として解釈します。したがって、バッククオート()を含む文字列を引数として与えると、該当するカラム名が見つからないためエラーが発生します。
> 
> これらの違いは、PythonのPandasライブラリやSQLのようなデータ操作の抽象化を提供するApache Sparkの仕様に由来します。これらのメソッドは、様々なデータ操作の一部として設計されており、それぞれが異なるコンテキストで使用されるため、同じ引数が異なる解釈をされることがあります。
> 
> したがって、`dropDuplicates`メソッドを使用する際は、バッククオート(``)を含まないカラム名を引数として与えてください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/285cdb68-cb7f-028f-21c0-704346f4efa1.png)


## エラーの再現方法

### 事前準備

#### 検証用のデータフレームを作成

```
schema = """
`名前` string,
`数値` int
"""
data = [
    {
        "名前": "あ",
        "数値": 1,
    },
    {
        "名前": "い",
        "数値": 2,
    },
    {
        "名前": "う",
        "数値": 3,
    },
]

df = spark.createDataFrame(data, schema)
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/e9ecf589-bcea-0891-8f72-cd6ab2814849.png)

### `select`メソッドの動作確認

```python
df.select(["`名前`", "`数値`"]).display()
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/7da420bf-7f6f-74c2-de77-f7e05e343c09.png)


### エラーの再現

#### `dropDuplicates`メソッドのエラー

```python
col_list = ["`名前`"]
df.dropDuplicates(col_list).display()
```

> AnalysisException: Cannot resolve column name "`名前`" among (名前, 数値).

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/34dda1ba-f0f1-9e01-36ab-a86db66fb911.png)

#### `join`メソッドのエラー

```python
col_list = ["`名前`"]
df.join(df, col_list, "INNER").display()
```

> AnalysisException: [UNRESOLVED_USING_COLUMN_FOR_JOIN] USING column `名前` cannot be resolved on the left side of the join. The left-side columns: [`名前`, `数値`].

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/70e890b8-d3f0-e2b1-546b-614798df41eb.png)

## 対応方法

### `dropDuplicates`メソッドのエラーへの対応方法

```python
col_list = ["`名前`"]
trimed_col_list = [col.lstrip("`").rstrip("`") for col in col_list]
df.dropDuplicates(trimed_col_list).display()
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/8dd68ab8-7ac5-3a10-2d2d-ae899fda4b5e.png)

### `join`メソッドのエラーへの対応方法

```python
col_list = ["`名前`"]
trimed_col_list = [col.lstrip("`").rstrip("`") for col in col_list]
df.join(df, trimed_col_list, "INNER").display()
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/1c36cc5d-ed38-6886-ebd5-39bf4a232f40.png)