あれもPython,これもPython

Pythonメモ※本サイトはアフィリエイトを利用しています

文字列のバイグラムをとりたい(自分で書く)

ユーザー入力のデータを扱っていると、
ゆらぎが存在し、そこからも傾向を取り出したいことがあります。

そうした、文書と言うほどではないカテゴリデータの場合、形態素解析では十分に情報がとりださせないことが多いです。

そこで、最初や最後の文字、文字の長さなどで情報を増やしていくのですが、一番自分が信用しているのがngramです。
特に、日本語/中国語はn=2(バイグラム)でなんとなく、使える情報が取り出せる感じがします。

毎回書くのが面倒なのでまとめておきます。

挙動

targets= ['python','ruby','go']

char_ngram(targets,n=3)

こんな感じで入れると、こんな感じで帰ってきます。

   words   char1   char2   char3   ngram
0  python  p   y   h   pyh
1  python  y   t   o   yto
2  python  t   h   n   thn
3  python  h   o   NaN ho
4  python  o   n   NaN on
...

コード

いろいろ考えたのですが、普通にforループを回すのが一番楽と気づきました。

こんな関数を作っておきます

import pandas as pd

#単語のリストとnを渡す
def char_ngram(words,n=2):

  #文字で分割してDFに
  res = []
  for word in words:
    res.extend([(word,n) for n in word])

  df = pd.DataFrame(res,columns = ['words','char1'])

  #nの数だけずらしたカラムを生成
  f = lambda x,i : x.shift(-1 * (i+1))
  for i in range(n-1):
    df['char' + str(i+2)] = df.groupby('words')['char'+str(i+1)].transform(f,i)

  #n-gramを結合したカラムを生成
  cols = [c for c in df.columns if c.startswith('char')] #charのカラムだけ用意
  unit = lambda x : x.str.cat(),
  df['ngram'] = df[cols].apply(unit,axis=1)

  return df

ちょいちょい細かいテクニックを使っています。
transformなんかはwindow関数を再現する時に使いましたが、今回もgroupbyで範囲を絞りつつ、大元の行数を崩さないために使っています。

esu-ko.hatenablog.com