BigQueryを使った簡単なデータの前処理

BigQueryを使った簡単なデータの前処理のメリットと具体的な方法を紹介する．一応備忘録を兼ねている．

# はじめに
この記事ではテキストデータに対する簡単な前処理をBigQueryで行う方法やそのメリットについて紹介する．今回の記事ではSQLを使って前処理を行っているので，あまり複雑な処理は行わない（筆者はSQL初心者なので"行えない"が正しい...）ため"簡単な"前処理と書いている．
ちなみに，BigQueryを使いこなしている人やそもそも前処理を行うための基盤が整っている人はあまりこの記事を読んでも得られるものはないかも．

# BigQueryで前処理を行うメリット
## 大規模データに対する処理能力が高い
BigQueryでは「カラム型データストア」と「ツリーアーキテクチャ」を採用しており，**大規模なデータに対する処理能力が非常に高い**．カラム型データストアはその名の通り，列単位でデータを保存するためデータの圧縮率が非常に高い．そしてツリーアーキテクチャはクエリを自動で分散して処理してくれる機能となっている．
これらはクエリを発行する際に気を付けないといけないことではあるが（例えば，列単位で保存されているためバイト数の多い列はあまり取得しない，など）大規模データを扱う上では非常に有利となる．

## 一般的なDBサーバと違い「サーバの維持費」が無料
BigQueryはCloud SQLなどの一般的なDBサーバとは違い，サーバを確保するといったことがないので処理をほとんどしていなくても**維持費がかかるということがない**．
前処理のためにある程度のメモリを積んだインスタンスを確保するとなると，方法によっては一カ月で5,000円ほどかかってきてしまう場合がある．（これは前処理のためにインスタンスを常に確保するという正直ほぼ使わないだろう方法だが...Kubernetesなどを使って賢くやれば費用はかなり抑えられる）
BigQueryで前処理を行うことでわざわざサーバを確保するコストをがぶくことが出来る．

## 課金のシステムが「処理されたバイト数」
BigQueryの課金対象となるのが基本的には「保存しているデータ量」とクエリを実行した際の「処理したバイト数」の2つとなっており，前者のほうはGCSと同じ料金なためそこまで気にするほどの金額とはならない．
問題なのは後者の「処理されたバイト数」で，こちらは一見BigQueryで前処理をすればかなり課金されてしまうように見えるが，実はこの「処理したバイト数」というのは処理の内容などではなく何バイト処理をしたかという数値になる．そのため，**大量のデータに対して文字列操作や数値計算を行っても普通にデータ取得したときと課金額が変わらない**．（処理によっては変わる可能性あり．まだ文字列操作などしか確認できていない．）
つまり，前処理しなければ損とも言える（？）．
ちなみに**BigQueryは毎月1TBまでは処理したバイト数の無料枠**となっているので，使わないと損だと思う．

# BigQueryによるテキストデータの簡単な前処理
## 使用するデータ
- 件数：250,000件
- データ形式：日本語文章＋その文章の感情のラベル
- データソース：Twitter

このデータは感情分析のモデル学習に使用するデータセット（の加工前）となっている．

## 加工前のデータ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176691/996f75ac-77ee-0b92-6c26-a426165728ed.png)

加工前のデータは文章（textフィールド）に絵文字やメンション（@○○など）・改行などがありかなり使いずらいデータなのが見てわかる．

## 絵文字の削除
まずは文章内の絵文字をすべて削除していく．BigQueryの関数であるregexp_replace（正規表現でマッチした文字列を削除）を使っていく．
UnicodeではU+2600-26FF，U+2700-27BF，U+10000-1FFFFあたりに絵文字が固まっている．後半のU+10000からは絵文字以外にもいろいろあるが，テキストデータとして使うにはかなり面倒だと思うので絵文字と一緒に削除してしまう．
ちなみに4バイト絵文字を消す際に\U00010000を\u00010000と小文字のuにしてしまうとうまく動かない（大文字だとUTF-32と判定される？）．実はここら辺はあまり理解していない...

```sql:絵文字の削除
CREATE TEMPORARY FUNCTION cleaning_text(text STRING)
AS (
    regexp_replace(
        text,
        '[\u2600-\u26FF]|[\u2700-\u27BF]|[\U00010000-\U0001FFFF]',
        ''
    )
);

SELECT
    cleaning_text(text),
    created_at
FROM 
    `project_id.dataset.table`
WHERE 
    created_at
BETWEEN 
    '2021-10-26 17:00:09 UTC'
AND 
    '2021-10-26 18:16:09 UTC'
ORDER BY 
    created_at
LIMIT 
    100
```

実行結果：
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176691/3ea0d28f-7049-ef5a-95f0-f266f63541ea.png)

加工前と比べて絵文字がなくなっているのが分かる．
次にメンションを削除する．

## メンションの削除
次にメンションを削除する．これは簡単でさっきの正規表現の中に@○○（英数字・アンダーバー1~15文字）を追加してあげればいい．

```sql:メンションの削除
    regexp_replace(
        text,
        # 変わったのは下の部分のみ
        '[\u2600-\u26FF]|[\u2700-\u27BF]|[\U00010000-\U0001FFFF]|@[0-9a-zA-Z_]{1,15}',
        ''
    )
```

実行結果：
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176691/47d7352b-698c-ed6d-ff36-1312068ac99e.png)

メンションだけが消えているのが分かると思う．ついでにさっきの正規表現に"\n"を追加して改行も消す（使用するアルゴリズムによっては改行はあってもいいかもしれない）．

実行結果：
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176691/f69b88d1-2dbd-a9c5-825a-5ffd89895658.png)

これで一応学習に使えそうではあるデータになったように見える．

# まとめ
内容めっちゃ薄いけど，とりあえずBigQueryを使ったテキストデータの簡単な前処理について書いた．正直ここまでは形式の決まっているものを削除しただけなので簡単なのだが，ここからさらにハッシュタグの削除やアスキーアートのような意味のない文章のふるい分けなどが必要だと思う．あと極端に短い・長い文章を削除したり．
やんなきゃいけないことはめっちゃあるので，備忘録としてまた記事作る気がする．