Hatena::ブログ(Diary)

盆栽日記

2016-11-28

またおまえかtibble

従来の集計処理をdplyrに置き換えていく中でこういうエラーが出た。

Error in function_list[[k]](value) : 
  (list) object cannot be coerced to type 'double'

これまでは問題なく集計処理できていたのにdplyrを使った途端このエラーである。

問題を突き止めていくうちにtibble形式が原因であることがわかった。

以下は今回出たエラーを再現したものではないが、tibble形式に変わることで扱い方に注意すべき例として挙げた。

> library(dplyr)
> tbl_iris <- as.tbl(iris)

> as.character(tbl_iris[1:3,"Species"])
[1] "c(1, 1, 1)"

> as.character(iris[1:3,"Species"])
[1] "setosa" "setosa" "setosa"

2016-11-10

岡村靖幸のWikipedia記事から人名を抽出する

最近スクレイピングしてないので素振りをすることにした。

今回は岡村靖幸のWikipedia記事内の経歴の項目における人名を抽出して眺めてみたい。

以下の流れで進める。

1. ページを眺めて、スクレイピングの対象とする文章が埋め込まれている場所を特定する。

2. rvestパッケージを使ってスクレイピングを実行する。

3. gooラボの固有表現抽出APIを使って人名を抽出する。

ページを眺めて、スクレイピングの対象とする文章が埋め込まれている場所を特定する。

まず岡村靖幸のページを眺めてみよう。

https://ja.wikipedia.org/wiki/%E5%B2%A1%E6%9D%91%E9%9D%96%E5%B9%B8

衝撃的なデビュー、瞬く間にスターダムにのし上がった後の没落、3度の逮捕からの不死鳥のごとき復活、様々な想いが私の胸を去来する。

さて、記事一覧において、各記事のURLが埋め込まれている構造(XPath/CSSセレクタ)についてはChromeの「検証ツール」を用いて取得する。

この情報は後ほどrvestパッケージを用いて各記事のURLを抽出する際に用いる。

たとえば「経歴」と書かれた部分のXPathを取得したい場合は、「経歴」の上で右クリックして「検証」を選ぶ。

そして、「経歴」という文字の上でさらに右クリックをしてCopy→Copy XPathを選ぶ。

すると以下のような文字列が取得できるだろう。これはこのページにおける「経歴」の位置をXpathで取得した結果である。

//*[@id=".E7.B5.8C.E6.AD.B4"]

該当部分における実際のHTMLドキュメントは以下のようになっている。

<span class="mw-headline" id=".E7.B5.8C.E6.AD.B4">経歴</span>

idはドキュメントの中で一意である。

ここではidが".E7.B5.8C.E6.AD.B4"という形で指定されている。

したがって、このidを指定するだけで位置が一意に決まる。これをXpathで指定すると先の例になる。

Xpathの指定方法については以下のチートシート等を参考にすると良い。

http://aoproj.web.fc2.com/xpath/XPath_cheatsheets_v2.pdf

さて、今回欲しいのは「経歴」のドキュメントのみである。

ソースを見る限り、「経歴」があらためてdivタグで囲われてidが振られているなんて便利なことはないなので地道にpタグ以下の文章の順番を確認して抽出していく必要がありそうである。

「経歴」の最初と最後、それぞれの文の上で検証ツールを通してXPathを取得すると以下の結果が得られる。

//*[@id="mw-content-text"]/p[4]
//*[@id="mw-content-text"]/p[42]

さて、このXPathを用いてrvestパッケージによるスクレイピングを実行してみよう。

rvestパッケージを使ってスクレイピングを実行する。

rvestパッケージによるスクレイピングは以下の手順で進める。

1. HTMLドキュメントの読み込み

2. 目標とする情報が埋め込まれている構造を指定して情報を抽出する(今回はXPathを利用する)

3. データを整形する

library("rvest")
u <- "https://ja.wikipedia.org/wiki/%E5%B2%A1%E6%9D%91%E9%9D%96%E5%B9%B8"
h <- read_html(u)
txt <- h %>% html_nodes(xpath='//*[@id="mw-content-text"]/p') %>% html_text()
txt <- txt[4:42]
txt <- iconv(txt, from="UTF-8",to="CP932")
txt <- paste(txt, collapse="")

HTMLドキュメントの読み込みにはread_html関数を用いる。

さらにhtml_nodes関数に先ほど取得したXPathを指定して情報を抽出する。

Xpathでは、Rの要素指定のように//*[@id="mw-content-text"]/p[4:42]のような指定はできないので、いったん要素の番号を指定せずに抽出し、Rのベクトルに変換した後あらためて要素の番号を指定している。

最後に文字コードを変換した後(使用OSがWindowsなので)、全ての文章をpaste関数で結合している。

これで目標とする部分が抽出できた。

gooラボの固有表現抽出APIを使って人名を抽出する

最後に収集したテキストから固有表現を抽出する。

今回はgooラボの固有表現抽出APIを利用する。

https://labs.goo.ne.jp/api/jp/named-entity-extraction/

利用にあたって登録が必要であり、この際Githubアカウントが求められる。

Githubアカウントさえ持っているなら登録は一瞬であり、即時にアプリケーションIDが発行される。

このアプリケーションIDをメモっておく。

なお、本APIを利用する際はクレジット画像の表示が必要とのことなので以下に表示する。

http://u.xgoo.jp/img/sgoo.png

さて、固有表現抽出APIのようなwebAPIを使う際は、httrパッケージを用いる。

固有表現抽出APIの説明を確認すると、リクエスト先URLにhttps://labs.goo.ne.jp/api/entity

が、そしてリクエスト方法としてはapplication/x-www-form-urlencodedもしくはapplication/json形式でのPOSTを受け付けるとある。

また、リクエストパラメータとしてはapp_idとして先ほど取得したアプリケーションIDと、sentenceとして固有表現を抽出したい文章が必須であるとも書かれている。

以上の情報をhttrパッケージを用いて書くと以下のようなコードになる。

POSTメソッドなのでPOST関数を用いて、そのurl引数にリクエスト先URLを指定し、application/json形式でPOSTするためにencodeに"json"を指定している。

リクエストパラメータについてはbody引数に指定する。リクエストパラメータが複数ある場合はlistでまとめる。

library("httr")
app_id <- "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
res <- POST(url="https://labs.goo.ne.jp/api/entity",
            encode="json",
            body=list(app_id=app_id,sentence=txt))

取得した結果にはステータス等様々な情報が含まれている。

固有表現抽出APIの適用結果のみ抽出したい場合はhttrパッケージのcontent関数を用いる。

content関数のtype引数に何も指定しないと、データのcontent-typeを見てよしなに変換してくれる。

今回はcontent-typeがapplication/jsonなのでjsonliteパッケージのfromJSON関数が適用されリストに変換される。

リストにはrequest_idと固有表現の抽出結果であるne_listが含まれているので、ne_listを抽出し、データフレームに変換し、列名を付与している。

固有表現にはART(人工物名)、ORG(組織名)、PSN(人名)、LOC(地名)、DAT(日付表現)、TIM(時刻表現)があり、今回は人名が欲しいのでPSNのみを抽出した。

library("dplyr")
ne_list <- do.call("rbind",content(res)$ne_list)
colnames(ne_list) <- c("word", "type")
ne_list %>% filter(type=="PSN") %>% unique()

結果は以下の通りである。

> ne_list %>% filter(type=="PSN") %>% unique()
word type
1      渡辺美里  PSN
2      吉川晃司  PSN
3      鈴木雅之  PSN
5      白井貴子  PSN
6            TM  PSN
8        尾崎豊  PSN
9          岡村  PSN
13     金山一彦  PSN
14 チェッカーズ  PSN
18     川本真琴  PSN
19       西田彩  PSN
20     黒田倫弘  PSN
21     朝日美穂  PSN
22   直枝政太郎  PSN
23     直枝政広  PSN
25         直枝  PSN
26         石野  PSN
29     小林武史  PSN
30     岡村靖幸  PSN
39          MEG  PSN
42     大沢伸一  PSN
46   木村カエラ  PSN
48     坂本龍一  PSN
49     小出祐介  PSN
50         久保  PSN
51     久保みね  PSN

久保とか久保みねというのはテレビ番組「久保みねヒャダ」を抽出しそこなかった結果ですね。

それにしても川本真琴のデビュー曲「愛の才能」は後奏が突然岡村靖幸ソロになるんだよなあ、とか

尾崎豊と岡村靖幸がぐだぐだになりながら二人で歌っている広島のライブ映像は「若さ!!!!!!!!!!!!!」という感じで実に良いんだよなあ、とか

岡村靖幸がなぜか変名で作曲した川本真琴の「FRAGILE」は初めて聴いた時は「なげえ」としか思わなかったけど今聴くとなんだか胸に迫るんだよなあ、とか

様々に思い起こされる結果でした。

2016-11-09

XLConnectにtblクラスのデータをつっこむとエラーになるから注意

XLConnectにdplyr等で操作した後のtblクラスのデータをつっこむとエラーが出る。

たとえば以下のようなコードを実行する。

smp <- loadWorkbook("sample.xlsx", create=TRUE)
smp["サンプル"] <- iris %>% group_by(Species) %>% tally()

こういうエラーが出る。

Error in doTryCatch(return(expr), name, parentenv, handler) :

Conversion to character for column Species failed! Check the class.

as.data.frameしてやれば解決する。

smp <- loadWorkbook("sample.xlsx", create=TRUE)
smp["サンプル"] <- iris %>% group_by(Species) %>% tally() %>% as.data.frame()

データフレームだと今までエラー出なかったのに!という時はだいたいこのtblが原因なので困ったらas.data.frameと覚えておくと良い。

2016-10-17

Google Cloud Vision APIを使ってAKBのデータを把握する

こんな話がある。

こういうのを見たらGoogle Cloud VisionでとりあえずOCRである。

CROWD_VISION_KEY <- "YOUR KEY"

library("httr")

f <- "https://pbs.twimg.com/media/Cu9cVUlUkAAiHPL.jpg"
tmp <- tempfile()
download.file(f, tmp)
img <- readBin(tmp, "raw", file.info(tmp)[1, "size"])
u <- paste0("https://vision.googleapis.com/v1/images:annotate?key=", CROWD_VISION_KEY)
body <- list(requests = list(image=list(content=img), 
                             features=list(type="TEXT_DETECTION")
                             )
             )
res <- POST(url=u,
            encode="json",
            body=body,
            content_type_json()
)
res <- content(res)
cat(res$responses[[1]]$textAnnotations[[1]]$description[1])

結果はこうなる。

あとはなんやかんや加工すれば使えそう。

> cat(res$responses[[1]]$textAnnotations[[1]]$description[1])
「Excel」を使ってデータを入力してみよう
6.2 「Excel」
表6.1 AKB48チームA
チームAメンバーリスト(20157月現在)45 lap-身長
小嶋陽菜
27 | 164
高橋みなみ | 24 | 148.5
大家志津香 1231164
宮崎美穂
22 | 159
横山由依
22 | 158
中村麻里子 | 21 | 161
島崎遥香
21 | 157
小笠原茉由 | 21 | 151
前田亜美
20 | 165
中西智代梨 | 20 | 160
小嶋菜月
20 | 154
佐々木優佳里| 19 | 158
入山杏奈
19 | 157
田北香世子 | 18 | 155
宮脇咲良 | 17 | 160
岩田華怜
17 | 159
白問美瑠
17 | 155
平田梨奈
17 | 149
谷口めぐ | 161150
16 | 159
山田菜々美 | 16 | 154
大和田南那 1151154
樋渡結依
15 | 151
西山怜那
14 | 151
用するデータは,みなこのような「表」の形をしています
統計
で利
EArte !「身長1 と かれ
書
ーT 197ーー160-194-198 1T 160 150 199
一長一
4555554 6 6 5 551111111111
64655655
!齢! | 9 | | 8 | 7 | | 6 | 5 |
! 27-24 23 22 22-21-21-21-20 20 20-19 19 8777-76-655u
22 2) 2 2 2 2 2 1 1 1 1 | 1 | 111-111
4: 2-22-2-2
-- 1 1 1 1 1 1
1 | 1 1 |

いちいちhttr使うのもちょっとという人は、Rooglevisonというパッケージもあるので、使ってみるとよい。

2016-10-11

plotlyで複数の図を並べて描く

plotlyで複数の図を並べて描くときはsubplot関数を用いる。

subplotというキーワードさえ覚えておけばあとは検索するといくらでも例が出てくる。

library("ggplot2")
library("plotly")
g <- ggplot(economics, aes(x = date, y = uempmed)) + geom_line()
pg <- plotly_build(g)
subplot(pg, pg)

f:id:dichika:20161011151652p:image