LoginSignup
6
5

More than 5 years have passed since last update.

ディレクトリ構造を持つjsonファイルたちを形態素解析をしてcsvに一括変換

Last updated at Posted at 2018-09-27

複雑なディレクトリ構造を持つjsonファイルをcsvに一括変換したい

日付毎に記録されたjsonデータをcsvに変換したいとする。
ここでまず以下の方法が考えられる
1. json⇒csvに変換してくれるサイトを利用
2. pythonの変換ライブラリ(pd.read_json()など)を使用する

しかしながら、このどちらも以下の問題を抱えていた
1...一度に変換できる容量に制限アリ&同様のディレクトリを手作業で作り直すのが面倒
2...pd.json_read()でエンコーディングエラー多発

そのため、今回はjson⇒numpy⇒csvとワンクッション置いて必要な情報のみを取り出した

1. 重for文をさけて2018-01-02.jsonのようなファイルパスを表現する

まずこのように内包表記を定義します

#内包表記で重複for分を避ける
    years = range(2018,2019)
    months = range(1, 13)
    days = range(1,32)

    combinations = [(year, month, day)
                    for year in years
                    for month in months
                    for day in days]

この内包表記を利用することで、for文は一度で表現することができます

    for year, month, day in combinations:
        #ファイル名に合うようにゼロ埋めを実行. ex)2018-09-01
        date = str(year) + '-' + str(month).zfill(2) + '-' + str(day).zfill(2)
        #pathの生成を自動化(ディレクトリ名もそのうち自動で)
        path = "C:/Users/名前/Desktop/json/目的のディレクトリ/" + str(date) + ".json"

2.ファイルパスの判定とjson⇒numpy

1でjsonファイルのpathを自動生成することが出来ました。しかしながら、生成した日付のjsonファイルがない場合エラーを吐いてしまいます。
そこで、os.path.isfile(path)という関数を使用することでpathの存在を判定し、同時にjson⇒numpyへ変換してしまいましょう

if(os.path.isfile(path)):
            #日付代わりにカウンターを更新。これが行の値になる
            count += 1
            #以下、encodingを指定しないとエラーを吐く
            f = None
            f = open(path, 'r', encoding="utf-8")
            #ファイルのデータを取得
            json_data = json.load(f)

            #numpyにぶち込む
            data_ = []
            data_ = np.array(json_data)

elseは今回特に指定しませんでした。

3.データのクリーニング(今回は自然言語処理用)と単語抽出

今回使用するjsonファイルの特徴を見てクリーニング方法を検討します
テキストを含むjsonファイルはすべて
{'type': 'message'
から始まっていたので、こちらを判定に利用しました

 #data_をクリーニングしてから全体の配列に格納
            for i in range(len(data_)):
                data_text = str(data_[i])
                text_ = data_text.split(',')
                #メッセージだけを取り出す
                if(text_[0]=="{'type': 'message'"):
                    rm = "'text': "
                    pure_text = clean_text(text_[2].replace(rm,''))
                    print(pure_text)
                    tokens = t.tokenize(pure_text)
                    for token in tokens:
                        partOfSpeech = token.part_of_speech.split(',')[0]
                        # 今回抽出するのは名詞だけ。(もちろん他の品詞を追加、変更、除外可能。)
                        if partOfSpeech == u'名詞':
                            each_data.append(token.surface) # token.surfaceは表層形(語彙)。詳しくはこちら...http://ailaby.com/janome/
                    #抽出した名詞たちをdata_allに追加していく。
                    data_all.append(each_data)
                    each_data = []

4. 形態要素解析後のデータをexcelファイルに出力

    ## 文章を形態素毎に分割したデータをいれるエクセルファイル作成(今回は名詞のみ)
    # 例)C:\Users\〜\Documents\〜
    # パスの前のrは省略しない
    data_book = xlsxwriter.Workbook("例)C:\Users\〜\Documents\ファイル名1.xlsx")
    # シート作成・変数定義
    data_sheet = data_book.add_worksheet('data')
    for row in range(len(data_all)):
        for i in range(len(data_all[row])):
            data_sheet.write(row, i, data_all[row][i])
    # エクセルを保存
    data_book.close()

5. 名詞の頻度ランキングをexcelファイルに出力

    #すべての語彙を同じ配列に格納
    chain_data = list(chain.from_iterable(data_all))
    c = Counter(chain_data)

    result_ranking = c.most_common(1000) # 出現頻度100位までを変数に格納
    # 語彙出現ランキングを記述するエクセルを作成(パスはファイルを作成したいところに)
    result_book = xlsxwriter.Workbook("例)C:\Users\〜\Documents\ファイル名2.xlsx")
    # resultという名前のシートを作成
    result_sheet = result_book.add_worksheet('result')
    for row in range(len(result_ranking)):
        for i in range(len(result_ranking[row])):
            result_sheet.write(row, i, result_ranking[row][i])
    # エクセルを保存
    result_book.close()

6.おつかれさまでした

これで大体のjsonファイルをcsvで取り扱えそうですね

コード全文

analysis.py
import pandas as pd
import json
import os
import os.path
import numpy as np
from janome.tokenizer import Tokenizer
import xlrd
import xlsxwriter
from collections import Counter
from itertools import chain

#テキストデータをクリーニングする関数
def clean_text(replaced_text):
    """
    \S+ matches all non-whitespace characters (the end of the url)
    :param html_text:
    :return:
    """
    replaced_text = re.sub(r'http\S+', '', replaced_text)
    replaced_text = re.sub(r'[@@]\w+', '', replaced_text)  # メンションの除去
    replaced_text = re.sub(r' ', ' ', replaced_text)  # 全角空白の除去
    replaced_text = re.sub(r'<', ' ', replaced_text)  # 全角空白の除去
    replaced_text = re.sub(r'>', ' ', replaced_text)  # 全角空白の除去
    return replaced_text

########################################################
#メイン処理
if __name__ == "__main__":
    #内包表記で重複for分を避ける
    years = range(2018,2019)
    months = range(1, 13)
    days = range(1,32)

    combinations = [(year, month, day)
                    for year in years
                    for month in months
                    for day in days]

    #内包表記で繰り返し処理
    #縦軸の日付をうまく行列に反映できないので、とりあえずカウンターを設置する
    count = 0
    data_all = [] #テキストファイルのまとめ先
    each_data = [] #語彙の格納先
    t = Tokenizer() #形態素解析用のモジュール
    #1.形態素解析
    for year, month, day in combinations:
        #ファイル名に合うようにゼロ埋めを実行. ex)2018-09-01
        date = str(year) + '-' + str(month).zfill(2) + '-' + str(day).zfill(2)
        #pathの生成も自動化(channelもそのうち自動で)適宜読み替えてpathを設定してください
        path = "C:/Users/ユーザー名/Desktop/json/目標のディレクトリ/" + str(date) + ".json"
        if(os.path.isfile(path)):
            #日付代わりにカウンターを更新。これが行の値になる
            count += 1
            #encodingを指定しないとエラーを吐く
            f = None
            f = open(path, 'r', encoding="utf-8")
            #print(f)
            #ファイルのデータを取得
            json_data = json.load(f)
            #numpyにぶち込む

            data_ = []
            data_ = np.array(json_data)

            #count行目に入る値
            print(count)
            #data_をクリーニングしてから全体の配列に格納
            for i in range(len(data_)):
                #print(data_[i])
                data_text = str(data_[i])
                #print(data_text)
                text_ = data_text.split(',')
                #メッセージだけを取り出す
                if(text_[0]=="{'type': 'message'"):
                    rm = "'text': "
                    pure_text = clean_text(text_[2].replace(rm,''))
                    print(pure_text)
                    tokens = t.tokenize(pure_text)
                    for token in tokens:
                        partOfSpeech = token.part_of_speech.split(',')[0]
                        # 今回抽出するのは名詞だけ。(もちろん他の品詞を追加、変更、除外可能。)
                        if partOfSpeech == u'名詞':
                            each_data.append(token.surface) # token.surfaceは表層形(語彙)。詳しくはこちら...http://ailaby.com/janome/
                    #抽出した名詞たちをdata_allに追加していく。
                    data_all.append(each_data)
                    each_data = []

    ###########################################################
    ## 文章を形態素毎に分割したデータをいれるエクセルファイル作成(今回は名詞のみ)
    # 例)C:\Users\〜\Documents\〜
    # パスの前のrは省略しない
    data_book = xlsxwriter.Workbook("例)C:\Users\〜\Documents\ファイル名1.xlsx")
    # シート作成・変数定義
    data_sheet = data_book.add_worksheet('data')
    for row in range(len(data_all)):
        for i in range(len(data_all[row])):
            data_sheet.write(row, i, data_all[row][i])
    # エクセルを保存
    data_book.close()


    ############################################################
    #すべての語彙を同じ配列に格納
    chain_data = list(chain.from_iterable(data_all))
    c = Counter(chain_data)

    result_ranking = c.most_common(1000) # 出現頻度100位までを変数に格納
    # 語彙出現ランキングを記述するエクセルを作成(パスはファイルを作成したいところに)
    result_book = xlsxwriter.Workbook("例)C:\Users\〜\Documents\ファイル名2.xlsx")
    # resultという名前のシートを作成
    result_sheet = result_book.add_worksheet('result')
    for row in range(len(result_ranking)):
        for i in range(len(result_ranking[row])):
            result_sheet.write(row, i, result_ranking[row][i])
    # エクセルを保存
    result_book.close()
6
5
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
5