【自然言語処理】テキストデータにラベル付けするサイトを作った

:::note
このサイトのお話をしています。
https://konbraphat51.github.io/Text-Labeling/
:::

## この記事は
極性分析や文書分類などで、テキストデータをラベリングしたいタイミング、ありますよね。

そこで、冒頭の手軽のラベリングできるサイトを作りました。
![sasa_20230207_180437.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2602537/1c8a9eb8-d377-4708-cd00-51b49bb9754a.gif)

## 実装
### 全体コード
全体コードはこの通りです。
```html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div id = "define">
        対象ファイル
        <input type="file" id = "target_file">

        <br>

        開始インデックス（１スタート）
        <input type="number" id = "start_index" value = "1" min="1">
        <br>
        <br>

        ラベル種類数
        <input type="number" id="label_types">
        <button onclick="register_label_n()">種類数決定</button>
        <br>

        <br>
        <br>

        <div id = "labels">
            <div class = "label_name_div" id = "label_name_div_1">
                ラベル名
                <input type = "text" class="label_name">
            </div>
        </div>      
        
        <br>
        <br>

        <button onclick="start_labeling()">ラベリングへ</button>
    </div>
    <div id = "labeling" style="display: none;">
        <div id = "text">

        </div>
        <div id = "label_buttons">

        </div>
        <button id="save_button" onclick="save_file()">ファイル保存</button>
    </div>
</body>
</html>

<script>
    //テキストファイル
    var target_texts
    var output_texts = ""
    var reading_index

    //ラベル数を登録
    function register_label_n(){
        //リセット
        let label_name_divs = document.getElementsByClassName("label_name_div")
        let label_name_divs_n = label_name_divs.length
        for (let cnt = 0; cnt < label_name_divs_n-1; cnt++){
            label_name_divs[1].remove()
        }

        //要素数を取得
        let label_types_input = document.getElementById("label_types")
        let types_n = label_types_input.value

        //追加
        if (types_n > 1){
            let label_div_1 = document.getElementById("label_name_div_1")

            for (let cnt = 0; cnt < types_n-1; cnt++){
                let label_name_div = label_div_1.cloneNode(true)
                label_name_div.id = ""
                label_div_1.after(label_name_div)
            }
        }
    }

    //ラベリングモード開始
    function start_labeling(){
        //ラベル決めモード終了
        let define_div = document.getElementById("define")
        define_div.style.display = "none"

        //ラベリングエリア表示
        let labeling_div = document.getElementById("labeling")
        labeling_div.style.display="block"

        //開始インデックスを取得
        let start_index_input = document.getElementById("start_index")
        reading_index = start_index_input.value -1

        //ラベル名を取得
        let label_names_input = document.getElementsByClassName("label_name")
        let label_names = []
        for (let cnt = 0; cnt < label_names_input.length; cnt++){
            label_names.push(label_names_input[cnt].value)
        }

        //ラベルボタンを用意
        let label_button = document.createElement("button")
        label_button.className = "label_button"
        let label_button_div = document.getElementById("label_buttons")
        for (let cnt = 0; cnt < label_names.length; cnt++){
            let this_label_button = label_button.cloneNode(true)
            this_label_button.innerHTML = label_names[cnt]
            this_label_button.value = label_names[cnt]
            this_label_button.onclick = set_label

            label_button_div.appendChild(this_label_button)
        }

        //テキストファイル読み込み
        target_file = document.getElementById("target_file").files[0]
        var reader = new FileReader()
        reader.readAsText(target_file)
        //読み込み完了時の処理
        reader.onload = function(ev){
            target_texts = reader.result.split("\n")
            get_line()
        }
    }

    function get_line(){
        let text_div = document.getElementById("text")
        text_div.textContent = target_texts[reading_index]
        
        //次の行を読む
        reading_index++
    }

    //ラベル設定
    function set_label(event){
        //対象のテキスト
        let text = document.getElementById("text").textContent
        //改行文字を全て置換
        text = text.split("\n")
        text = text.join("")
        text = text.split("\r\n")
        text = text.join("")
        text = text.split("\r")
        text = text.join("")

        //選択されたラベル
        let label = event.target.value

        //書き込む行
        let output_line = text+"\t"+label+"\n"

        //記入
        output_texts += output_line

        //次の行へ
        get_line()
    }

    //ファイル保存
    function save_file(){
        //Blobを作る
        let blob = new Blob(output_texts.split(""), {type:"text/plan"}); // テキスト
        
        //仮想的なリンクを作成
        let link = document.createElement('a'); 
        link.href = URL.createObjectURL(blob); 
        link.download = 'output.txt'; //リンクをクリックしたら、ファイルをダウンロード
        
        //仮想的なリンクを仮想的にクリックして、ダウンロードする
        link.click();
    }
</script>

<style>
    #label_buttons button:nth-child(odd){
        width: 100px;
        height: 100px;
        background-color: pink;
    }

    #label_buttons button:nth-child(even){
        width: 100px;
        height: 100px;
        background-color: lightblue;
    }
</style>
```

### 仕組み
シーケンスとしては、
* 最初の初期設定モード
* ラベリングモード
があります。

#### 初期設定モード
これから設定するラベルの定義、および読むこむファイルのアップロードが行われます。

##### ファイルのアップロード
`inputタグ`の`type`に`file`を指定することでファイルのアップロードボタンを作ることができます。

読み込み部分を抜粋します。
```javascript
//inputタグ
target_file = document.getElementById("target_file").files[0]

//FileReaderの用意
var reader = new FileReader()

//対象の読み込みを開始
reader.readAsText(target_file)

//読み込み完了時の処理
reader.onload = function(ev){
    //target_texts（global変数の配列）に記録
    target_texts = reader.result.split("\n")

    //得られたテキストを表示
    get_line()
}
```

#### ラベル付けモード
ラベル付けをしていき、命令が下されたらファイルを保存します。

##### ファイルを保存する
javascript上に仮想的なダウンロードリンクを作成して、仮想的にクリックします。
```javascript
function save_file(){
    //Blobを作る
    let blob = new Blob(output_texts.split(""), {type:"text/plan"}); // テキスト
    
    //仮想的なリンクを作成
    let link = document.createElement('a'); 
    link.href = URL.createObjectURL(blob); 
    link.download = 'output.txt'; //リンクをクリックしたら、ファイルをダウンロード
    
    //仮想的なリンクを仮想的にクリックして、ダウンロードする
    link.click();
}
```

#### ページの公開
githubのレポジトリに`index.html`としてアップロードして、`github pages`で公開します