Fuwafuwa's memorandum

Fuwafuwa's memorandum

Data analysis, development, reading, daily feeling.
MENU

スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

SQL server: ナイーブベイズ分類器らしきものを実装する

テキストデータから分類できると様々な面で便利であったため。
ベイズを用いるのは実装が簡単そうだから。
テキストマイニングの御多分に漏れず大変計算量が多いため、可能な限り実装を簡素にする。

ちなみにテーブルをランダムサンプリングする方法として下記のようなものがある。
SQL server: 比率を指定してランダムサンプリング

通常であればテキストを形態素解析するところだが、面倒なので辞書データから名詞を検索し単語の生起確率を求める。
辞書データはipadicからNoun.csvを取り出してSQL serverにそのまま格納した。
上記ファイルには下記のカラムが格納されている。

表層形,左文脈ID,右文脈ID,コスト,品詞,品詞細分類1,品詞細分類2,品詞細分類3,活用形,活用型,原形,読み,発音

このうち「表層形・コスト・品詞・品詞細分類1・原型・読み・発音」のみ格納。
コストは単語が出現する際のコストであり数字が大きいほど出現頻度が少ない見込みとなる。

格納したいデータベースを右クリック→タスク→データのインポート
ウィザードでフラットソースからインポート。

なお、一般名詞辞書はデフォルトでは6万字あり、重そうなのでコストが小さいもののみを活用したい。
ここではコストが平均-標準偏差を下回る単語のみを格納。

DECLARE @avg_noun INT = (
	SELECT AVG(CONVERT(INT,[コスト]))
	FROM [辞書テーブル名] )
DECLARE @stdev_noun INT = (
	SELECT STDEV(CONVERT(INT,[コスト]))
	FROM [辞書テーブル名] )

IF OBJECT_ID('tempdb..#noun') IS NOT NULL
	DROP TABLE #noun

SELECT *
INTO #noun
FROM [dic_noun]
WHERE [コスト] < @avg_noun - @stdev_noun
textからlabelを付与するために下記で分類器らしきものを実装する。 \begin{equation}
P(label, text)
= P(label|text)P(text)
= P(text|label)P(label) \end{equation}

上記から下記が導ける。
\begin{equation}
P(label|text) = P(text|label)P(label) / P(text) \end{equation}

P(text)はどのlabelでも同じなので下記の分子のみを求める。
\begin{equation}
P(text|label)P(label) \end{equation}

下記でP(label)データを格納する。
DECLARE @text_n INT = (
	SELECT COUNT(text)
	FROM #sample )

IF OBJECT_ID('tempdb..#label_p') IS NOT NULL
	DROP TABLE #label_p

SELECT label,1.00*COUNT(text)/@text_n p
INTO #label_p
FROM #sample
GROUP BY label

次にP(text|label)を求めたい。
正確にはわからないため、下記でこれを代用する。 \begin{equation} P(text|label)=P(word|label)*P(word|label)*P(word|label)... \end{equation} また、P(word|label)は下記のように表せる。 \begin{equation} P(word|label) = P(word∩label)/P(label) \end{equation}
下記でP(word∩label)を求める。
--labelにおける単語の出現回数をテーブルに格納する
IF OBJECT_ID('tempdb..#label_noun') IS NOT NULL
	DROP TABLE #label_noun

SELECT label,noun,COUNT(noun) co
INTO #label_noun
FROM (
	SELECT noun.[表層形] noun,label
	FROM #sample data
	INNER JOIN #noun noun
		ON data.text LIKE '%'+[表層形]+'%'
	) tab
GROUP BY label,noun

--単語があるlabelのtextに含まれる確率
IF OBJECT_ID('tempdb..#word_label_p') IS NOT NULL
	DROP TABLE #word_label_p

CREATE TABLE #word_label_p
	( noun NVARCHAR(256)
	, label NVARCHAR(256)
	, p FLOAT )

DECLARE @label NVARCHAR(256)
DECLARE @co INT

DECLARE P_CURSOR CURSOR FOR
	SELECT label,SUM(co) co
	FROM #label_noun
	GROUP BY label
OPEN P_CURSOR
FETCH NEXT FROM P_CURSOR INTO @label,@co
WHILE @@FETCH_STATUS = 0
	BEGIN
		INSERT INTO #word_label_p
		SELECT noun,label,1.00*co/@co p
		FROM #label_noun
		WHERE label = @label
	
		FETCH NEXT FROM P_CURSOR INTO @label,@co
	END
CLOSE P_CURSOR
DEALLOCATE P_CURSOR
RETURN 
煩雑になるのでP(label)を省略する。

P(label)とP(word|label)を前述の公式に当てはめるだけだが、ここでは単語文書行列を作成していないため、乗算するとデータ数の多いlabelの生起確率が過剰に小さく見積もられることになる。かといって行列を作成するのもなんだか面倒なのでここでは単純に和とする。

下記でテストデータを入力する。
IF OBJECT_ID('tempdb..#test_result') IS NOT NULL
	DROP TABLE #test_result

CREATE TABLE #test_result 
	( id INT
	, text NVARCHAR(2048)
	, label NVARCHAR(256)
	, test_label NVARCHAR(256) 
	, prop FLOAT )

DECLARE @id INT
DECLARE @text NVARCHAR(2048)
DECLARE @label NVARCHAR(256)

DECLARE TEST_CURSOR CURSOR FOR
	SELECT id,label
	FROM #test
OPEN TEST_CURSOR
FETCH NEXT FROM TEST_CURSOR INTO @id,@label
WHILE @@FETCH_STATUS = 0
	BEGIN 
		INSERT INTO #test_result
		SELECT TOP 1 id,text,@label,data4.label,text_p+p prop
		FROM (
			SELECT id,text,label,SUM(word_p) text_p
			FROM (
				SELECT id,text,data1.noun,COUNT(data1.noun) wordcount,word.p word_p,word.label
				FROM (
					SELECT id,text,[表層形] noun
					FROM (
						SELECT *
						FROM #test 
						WHERE id = @id
						) data
					INNER JOIN #noun noun
						ON data.text LIKE '%'+noun.[表層形]+'%'
					) data1
				INNER JOIN #word_label_p word
					ON data1.noun = word.noun
				GROUP BY id,text,data1.noun,label,word.p
				) data2
			GROUP BY id,text,label
			) data3
		INNER JOIN #label_p data4
			ON data3.label = data4.label
		ORDER BY prop DESC
		
        FETCH NEXT FROM TEST_CURSOR INTO @id,@label
    END
CLOSE TEST_CURSOR
DEALLOCATE TEST_CURSOR
RETURN
以上。

スポンサーサイト

R: テキストを形態素解析し頻度計算

data = read.csv("file_name.csv")

library(RMeCab)
##名詞のみを取り出す
df <- docMatrixDF(data$text,pos=c("名詞"),minFreq=65)

apply(df,MARGIN=2,sum)

R: テキストを形態素解析してwordcloudを作成する

久しぶりにやったら方法がまったくわからなくなっていたのでメモ。

data = read.csv("file_name.csv")

library(RMeCab)
##名詞のみを取り出す
df <- docMatrixDF(data$text,pos=c("名詞"),minFreq=65)
##転置
df_t<-t(df)

library(wordcloud)
##scale:第1引数文字サイズ、第二引数文字間隔
wordcloud(colnames(df_t), df_t[,1], scale = c(5, .5),min.freq=1,
          random.order = FALSE, rot.per = .1, random.color = TRUE, colors = brewer.pal(8, "Dark2"))

Python: pandasで読み込む際のunicode errorの対処

普段は下記のようにencodingで対処していたのですが
Python: ファイルの読み込みの際のUnicodeエラーについて
utf-8、shift-jisのどちらでもエラーが出てしまい…。

困っていた所下記ページを見つけました。
pandasでread_csv時にUnicodeDecodeErrorが起きた時の対処

下記で。

import codecs

with codecs.open("file/to/path", "r", "Shift-JIS", "ignore") as file:
    df = pd.read_table(file, delimiter=",")

##テーブル確認    
df.head()

Python: mecab-pythonを使わずpython上で文字列を分かち書きする

Windowsでのmecab-python構築に挫折しました。
下記のようなサイトがあったのでそのままコピー&ペースト
mecab-pythonが無理だった人向けのモジュール[Windows編]

下記をmecab.pyとして保存。

# -*- coding: utf-8 -*-
 
class Mecab:
    """mecab-pythonのインストールに挫折した人用
    """
 
    def __init__(self, string=""):
        self.ma_list = self.cmd_mecab(string)
 
    def wakachi(self):
        """分かち書きの結果を返す(list)
        """
        wakachi_list = []
        for word_ma in self.ma_list:
            wakachi_list.append(word_ma.split('\t')[0])
        return wakachi_list
 
    def futsu_hyouki(self):
        """普通表記の結果を返す(list)
        """
        futsu_hyouki_list = []
        for word_ma in self.ma_list:
            word, ma_info = word_ma.split('\t')
            futsu_hyouki_info = ma_info.split(',')[6]
            word_futsu = ''
            if futsu_hyouki_info == '*':
                word_futsu = word
            else:
                word_futsu = futsu_hyouki_info
            futsu_hyouki_list.append(word_futsu)
        return futsu_hyouki_list
 
    def cmd_mecab(self, string='', opt=''):
        """
        Shell経由でmecabを実行して結果を取得
        (引き数)
          string str: 解析したい文字列
          opt str: mecabの解析オプション
        (返り値)
          stdout str: 解析した結果そのまま
        """
        import subprocess
        cmd = 'echo %s | mecab' % string.replace('\n', '')
        p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        stdout_data, stderr_data = p.communicate()
        stdout = str(stdout_data, encoding='Shift-JIS')
        words_with_ma_info = stdout.split("\r\n")
        words_with_ma_info.remove('EOS')
        words_with_ma_info.remove('')
        return words_with_ma_info
使い方。
import mecab
string = "国境の長いトンネルを抜けるとそこは雪国だった。"
m = mecab.Mecab(string)
 
# ただの分かち書きのリストを返す
m.wakachi()
#=> ['国境', 'の', '長い', 'トンネル', 'を', '抜ける', 'と', 'そこ', 'は', '雪国', 'だっ', 'た', '。']
 
# 普通表記のリストを返す
m.futsu_hyouki()
#=> ['国境', 'の', '長い', 'トンネル', 'を', '抜ける', 'と', 'そこ', 'は', '雪国', 'だ', 'た', '。']
cmd_mecabをいじると抽出品詞等も指定できそうですね。

該当の記事は見つかりませんでした。
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。