見出し画像

テキストマイニングの前処理まとめ

これまでやってきたテキストマイニングの前処理のまとめをします。
基本これまでの記事を1つの記事にまとめただけなので新しいことはあまりありません。

題材はツイッターのリツイートです。
サンプルをChat GPTで作りました。サンプルづくりにChat GPTは本当に便利です。

こんな感じです。

必要なパッケージのimportとデータフレーム作成

import pandas as pd #データフレームをつくる
import emoji  #絵文字を検出 import re  #正規表現 import contractions  #短縮形を直す import nltk #自然園後処理ツール
from nltk.tokenize import word_tokenize  #トークン化 import string #記号取得(string.punctuation)
from nltk.corpus import stopwords #stopwordsを取得

from nltk.stem import WordNetLemmatizer #lemma実行
from nltk.corpus import wordnet #品詞辞書

まずはサンプルテキストをデータフレームとして読み込みます。

df = pd.read_table("ClinicalTrial.txt")

表示させてみます。しかし、このままだと文字切れしてしまうので、set_optionで、表示制限を解除しています。

pd.set_option('display.max_colwidth', None)
df

読み込まれたデータフレームはこんな感じです。

正規化

まずは「RT」を取り除きます。データフレームに直接処理を適用するには、apply関数が便利なので、そのために処理をできる限り関数化していきます。
"RT"とその後ろのスペース(\s)の1回以上の繰り返し(+)を検出し、""に置き換えます。

def replace_retweet(tweet, default_replace=""):
  tweet = re.sub('RT\s+', default_replace, tweet)
  return tweet

applyを使って、この関数をdfの"tweet_text"に適用します。

df["Change_txt"] = df["tweet_text"].apply(replace_retweet)
df

この様にRTが消えましたね。apply関数は便利です。

次はユーザ名を一律"twitteruser"に変えます。
"\B@\w+”では、@”とその後ろの任意の英数字(\w)の繰り返し(+)を"Username"に置き換えています。

def replace_user(tweet, default_replace="twitteruser"):
  tweet = re.sub('\@\w+', default_replace, tweet)
  return tweet

これを適用させます。

df["Change_txt"] = df["Change_txt"].apply(replace_user)
df

ユーザ名が置換されました。

次は絵文字をテキストに変えます。これまで同様、関数化してそれをdfに適用していきます。

def demojize(tweet):
  tweet = emoji.demojize(tweet)
  return tweet

df["Change_txt"] = df["Change_txt"].apply(demojize)
df

今回は絵文字が満載です。この様に変わります。

ハッシュタグの#を取ります。

def replace_hashtag(tweet, default_replace=""):
  tweet = re.sub('#+', default_replace, tweet)
  return tweet

df["Change_txt"] = df["Change_txt"].apply(replace_hashtag)
df

全部小文字にします。

def to_lowercase(tweet):
  tweet = tweet.lower()
  return tweet

df["Change_txt"] = df["Change_txt"].apply(to_lowercase)
df

次に感情の乗った"loooook"などの繰り返しを正しく直します。

def word_repetition(tweet):
  tweet = re.sub(r'(.)\1+', r'\1\1', tweet)
  return tweet

df["Change_txt"] = df["Change_txt"].apply(word_repetition)
df

re.sub(r'(.)\1+', r'\1\1', tweet)が肝でしたね。
"."は任意の1文字、()でくくることでそれがグループとなります。
(\1+)で、(.)が1回以上繰り返される、つまり同じ文字が2つ以上続くことを意味します。
置換文字列 r'\1\1' は、正規表現パターンに一致した部分文字列を置き換える際に使用されます。ここでは、同じ文字が2回以上連続していた場合、その文字を2回続けて置き換えることを意味します。

LoooookがLookに変換

次に「!!!!!!!!!!!!!!!!!」のような記号の連続を「!」のようにします。

def punct_repetition(tweet, default_replace=""):
  tweet = re.sub(r'[\?\.\!]+(?=[\?\.\!])', default_replace, tweet)
  return tweet

df["Change_txt"] = df["Change_txt"].apply(punct_repetition)
df

[]は集合を表します。[\?\.\!]+は、"?", ".", "!"のいずれかが1つ以上繰り返していることを指します。ちなみにここで間違えて(?.!)+としてしまうと、"?.!"の塊が連続することになってしまいます。()はグループで、[]は集合です。
次の"?="は”肯定先読み”と言います。言葉だけだとなんのことだかわからないですね。
例えば、r'super(?=man)'だと、「super」の後に「man」が来た時にマッチします。(先行先読みという言葉からすると、「man」の前に「super」があると、マッチするのほうがいいかもしれません。)
なので、superwomanはマッチしません。
この場合だと、"?.!"のいずれかの1つ以上の後にまた"?.!"のいずれかがある状態を検出し、それを""で置き換えています。

「services!!!!!!」が「services!」になる

次にcontractions.fixを使って省略形を元に戻します。

def fix_contractions(tweet):
  tweet = contractions.fix(tweet)
  return tweet

df["Change_txt"] = df["Change_txt"].apply(fix_contractions)
df
we'veがwe haveに変わりました

そして記号、数字、ストップワードを消します。
clinical trialというワードがノイズになるので、それらをnew_stopwords = ["clinical", "trial"]でストップワードを指定して消すようにしました。必要な場合は、ここにワードを足していきます。

def custom_tokenize(tweet,
                    keep_punct = False,
                    keep_alnum = False,
                    keep_stop = False):
  
  token_list = word_tokenize(tweet)

  if not keep_punct:
    token_list = [token for token in token_list
                  if token not in string.punctuation]

  if not keep_alnum:
    token_list = [token for token in token_list if token.isalpha()]
  
  if not keep_stop:
    stop_words = set(stopwords.words('english'))
    # 新しいストップワードを追加
    new_stopwords = ["clinical", "trial"]

    # 新しいストップワードを既存のセットに追加
    stop_words.update(new_stopwords)
    stop_words.discard('not')
    token_list = [token for token in token_list if not token in stop_words]

  return token_list
記号やストップワードなどがなくなっています

最後にLemmatizationをします。
def get_wordnet_pos(word)で品詞情報を取り出します。
戻り値は、tag_dict.get(tag, wordnet.NOUN)です。品詞が辞書に存在する場合はtagに品詞の頭文字を返し、存在しない場合は、デフォルトで名詞(wordnet.NOUN)を返します。

そしてこの関数とlemmatizerを使って原形や単数形などに戻します。

def get_wordnet_pos(word):
    #品詞情報を取り出します
    tag = nltk.pos_tag([word])[0][1][0].upper()
    tag_dict = {"J": wordnet.ADJ,
                "N": wordnet.NOUN,
                "V": wordnet.VERB,
                "R": wordnet.ADV}

    return tag_dict.get(tag, wordnet.NOUN)


df['token_lemma'] = None

for index, row in df.iterrows():
    item=row["tokens"]
    lemmatizer = WordNetLemmatizer()
    token_list = []
    for token in item:
        token_list.append(lemmatizer.lemmatize(token, get_wordnet_pos(token)))
    df.at[index, 'token_lemma'] = token_list
df
discoveriesが discoveryになったりしています

元の文と比べて大分すっきりしましたね。
これでツイッターの情報をテキストマイニングする準備が整いました。

この記事が気に入ったらサポートをしてみませんか?