CORDEA blog

Android applications engineer

【Python】niconicoのコンテンツ検索apiを用いたアニメ間距離の可視化【R】

はじめに

3連休中あまりに暇だったので、動画に付いているタグによってアニメ間の距離はどう見えるかを調べた結果と作ったプログラム群。

今回対象としているアニメ

2010-2014年までのアニメ、823作品
Wikipediaから取得

主に使用しているライブラリ等

  • Python
    • scikit-learn
    • matplotlib
  • R
    • ctc
    • amap

やってみて感じたこと

問題点
  • 1700件までしか取得できないapi仕様
  • モノによってノイズが多く入り込む可能性がある
  • アニメによって数の差がかなりある
利点
  • 勢いのあるサービスであり、今後もデータが増える見込みがある
  • サムネイル画像習得による大規模画像解析が可能
  • 使い方次第でビッグデータになり得る


 
 

手順

ソースコードGitHubにあります。

アニメ一覧の取得(Wikipediaページのparse)

よく考えたらxmlが取得できるのでhtmlなどparseする必要はなかった...

python getDataFromWikipedia.py > animes_2010-2014.txt


 

検索結果の取得

apiにより取得したJSONをファイルに出力する
制限があるため、1秒間に2回、100件ずつ取得する。
また、検索結果が1700件以上あっても仕様上1700件までしか取得できない。

流れとしては以下の通り

  1. 取得対象名がタイトル/説明文/タグのいずれかに含まれているものを取得
  2. 検索結果として得られた動画に付いているタグをJSON形式で100件取得する。
  3. 100件をファイルに保存する

関係無い動画が取得される可能性はありますが、今回はそれもそのアニメ自体の影響力の強さと考えて特にフィルターをかけたりはしていません。

while read line;do;python getSearchResult.py $line;done < animes_2010-2014.txt

 

JSON→tsv 変換処理

変換処理によって横列がタグの出現回数, 縦列がコンテンツとするtsvファイルを作成する。

python tagParseJSON.py animes > animes_tag.tsv


変換処理によって832*354,117のデータが得られた


 

tSNEによる可視化

python mds_plot.py animes_tag.tsv
手法とパラメーターの探索

http://raw.cordea.jp/niconico/act1/explore_1.png


確認して一番良さげなPCA+t-SNEに絞る。

  1. PCAによる次元縮約
  2. t-SNEによる座標算出
  3. プロット


http://raw.cordea.jp/niconico/act1/explore_3.png



ココらへんのimageは全てこちらにあります。

結果

小さい画像
f:id:CORDEA:20141126221336p:plain


大きい画像
約33*33inchの非常に大きな画像ですのでお気をつけ下さい。


 

Hierachical clusteringによる可視化

Dendrogramを描きますが、これに関してはRの方が楽なのでRでやります。
ですがこのままプロットしたところで確認できるようなものにならないのは分かりきっているので、ctcライブラリを使用してDendroscope用のデータを作成します。
ちなみにctcライブラリはFedora 20で上手くインストール出来なかったのでこの部分はDebianを使っています。





出来上がったnewick.txtには以下のように何故かダブルクォーテーションが付いていてエラーを吐いたのでダブルクォーテーションを消します。

"(TIGER:82.6491092539521,(Fate%slZero:16.5373639856817,(Free!:18.3057504854552,
(Angel_Beats!:72.6269302031301,(STEINS%scGATE:69.8680393971481,
(R-15:31.1784166447384,(WORKING!!:46.0964574970961,(TARI_TARI:23.1396305160703,

[中略]

72.6269302031301):18.3057504854552):16.5373639856817):82.6491092539521);"

 

Dendroscopeで確認

ラベル一つ一つまで確認できるようなイメージを作成しようとしたところ、Debianでは強制終了し、WindowsではBusy状態で停止したため、適当な大きさで妥協した。

小さい画像

f:id:CORDEA:20141126221246p:plain



大きい画像
こちらも非常に大きな画像ですのでお気をつけ下さい。

 

まとめ

  • アニメ間の距離は続編が近い傾向にある。しかし続編間の距離が非常に遠くなったものもあった。
  • 動画数が非常に少ないアニメが2~3割あると見られ、それらが正常にプロットされておらず、何らかの閾値を設ける必要がある。
  • 説明変数が多すぎて上手くクラスタが構成されない
    • 次元縮約を行わない場合に、次元の呪いによる球面集中現象が見られた

そして結果をまとめるのが一番大変だった...
結果については色々と考察したのですが、やはり変数が多すぎることと手順に問題があること(距離の算出とか)から、検索数などでフィルターをかけて距離の算出法を見直せば何か見えてくるかもしれません。
いずれにしろかなりやりがいのあるデータですので今後も進めてまいります。

 

optparseでエスケープ文字を受け取りたい時

optparseはエスケープ文字をそのまま入力すると期待した結果と異なる挙動を示すことがあります。
これは一部のコマンド(sortなど)にも言えます。
しばらくしたら忘れてそうなのでメモ。

試しに次のようなプログラムを書いてみました。

from optparse import OptionParser

def optSettings():
    usage="hogehoge"
    version="0.0.1"
    parser = OptionParser(usage=usage, version=version)

    parser.add_option(
            "-a",
            action = "store",
            type   = "str",
            dest   = "test"
            )

    return parser.parse_args()

if __name__ == '__main__':
    options, args = optSettings()
    print >>>,options.test


このプログラムで次のように実行すれば"hogehoge"が帰ってきます。

% python optparse_test.py -a "hogehoge"
>>> hogehoge

'\t'や'\n'はこうなります。

% python optparse_test.py  -a "\t"
>>> \t


'\t'の場合は次のような入力によってタブを出力できます。
'#'はわかりやすいように入れているだけです。

% python optparse_test.py  -a $'#\t#'
>>> #	#


ちなみに始めに書いたsortコマンドの場合はこんな感じで。

% sort -t $'\t' -k 2 -nr

おわり。

Pythonで行/列名付きのmatrixを出力するいくつかの方法

正攻法, 変わり種など4つ。

  • 2014/11/12 追記

 方法3に問題が合ったので修正いたしました。
 それと方法4を追加。

 

今回出力したいのはこのようなマトリクス

	Ile	Val	Leu	Phe	Cys	Met	Ala	Gly	Thr	Trp	Ser	Tyr	Pro	His	Asp	Glu	Asn	Gln	Lys	Arg
Ile	IleIle	IleVal	IleLeu	IlePhe	IleCys	IleMet	IleAla	IleGly	IleThr	IleTrp	IleSer	IleTyr	IlePro	IleHis	IleAsp	IleGlu	IleAsn	IleGln	IleLys	IleArg
Val	ValIle	ValVal	ValLeu	ValPhe	ValCys	ValMet	ValAla	ValGly	ValThr	ValTrp	ValSer	ValTyr	ValPro	ValHis	ValAsp	ValGlu	ValAsn	ValGln	ValLys	ValArg
Leu	LeuIle	LeuVal	LeuLeu	LeuPhe	LeuCys	LeuMet	LeuAla	LeuGly	LeuThr	LeuTrp	LeuSer	LeuTyr	LeuPro	LeuHis	LeuAsp	LeuGlu	LeuAsn	LeuGln	LeuLys	LeuArg
Phe	PheIle	PheVal	PheLeu	PhePhe	PheCys	PheMet	PheAla	PheGly	PheThr	PheTrp	PheSer	PheTyr	PhePro	PheHis	PheAsp	PheGlu	PheAsn	PheGln	PheLys	PheArg
Cys	CysIle	CysVal	CysLeu	CysPhe	CysCys	CysMet	CysAla	CysGly	CysThr	CysTrp	CysSer	CysTyr	CysPro	CysHis	CysAsp	CysGlu	CysAsn	CysGln	CysLys	CysArg
Met	MetIle	MetVal	MetLeu	MetPhe	MetCys	MetMet	MetAla	MetGly	MetThr	MetTrp	MetSer	MetTyr	MetPro	MetHis	MetAsp	MetGlu	MetAsn	MetGln	MetLys	MetArg
Ala	AlaIle	AlaVal	AlaLeu	AlaPhe	AlaCys	AlaMet	AlaAla	AlaGly	AlaThr	AlaTrp	AlaSer	AlaTyr	AlaPro	AlaHis	AlaAsp	AlaGlu	AlaAsn	AlaGln	AlaLys	AlaArg
Gly	GlyIle	GlyVal	GlyLeu	GlyPhe	GlyCys	GlyMet	GlyAla	GlyGly	GlyThr	GlyTrp	GlySer	GlyTyr	GlyPro	GlyHis	GlyAsp	GlyGlu	GlyAsn	GlyGln	GlyLys	GlyArg
Thr	ThrIle	ThrVal	ThrLeu	ThrPhe	ThrCys	ThrMet	ThrAla	ThrGly	ThrThr	ThrTrp	ThrSer	ThrTyr	ThrPro	ThrHis	ThrAsp	ThrGlu	ThrAsn	ThrGln	ThrLys	ThrArg
Trp	TrpIle	TrpVal	TrpLeu	TrpPhe	TrpCys	TrpMet	TrpAla	TrpGly	TrpThr	TrpTrp	TrpSer	TrpTyr	TrpPro	TrpHis	TrpAsp	TrpGlu	TrpAsn	TrpGln	TrpLys	TrpArg
Ser	SerIle	SerVal	SerLeu	SerPhe	SerCys	SerMet	SerAla	SerGly	SerThr	SerTrp	SerSer	SerTyr	SerPro	SerHis	SerAsp	SerGlu	SerAsn	SerGln	SerLys	SerArg
Tyr	TyrIle	TyrVal	TyrLeu	TyrPhe	TyrCys	TyrMet	TyrAla	TyrGly	TyrThr	TyrTrp	TyrSer	TyrTyr	TyrPro	TyrHis	TyrAsp	TyrGlu	TyrAsn	TyrGln	TyrLys	TyrArg
Pro	ProIle	ProVal	ProLeu	ProPhe	ProCys	ProMet	ProAla	ProGly	ProThr	ProTrp	ProSer	ProTyr	ProPro	ProHis	ProAsp	ProGlu	ProAsn	ProGln	ProLys	ProArg
His	HisIle	HisVal	HisLeu	HisPhe	HisCys	HisMet	HisAla	HisGly	HisThr	HisTrp	HisSer	HisTyr	HisPro	HisHis	HisAsp	HisGlu	HisAsn	HisGln	HisLys	HisArg
Asp	AspIle	AspVal	AspLeu	AspPhe	AspCys	AspMet	AspAla	AspGly	AspThr	AspTrp	AspSer	AspTyr	AspPro	AspHis	AspAsp	AspGlu	AspAsn	AspGln	AspLys	AspArg
Glu	GluIle	GluVal	GluLeu	GluPhe	GluCys	GluMet	GluAla	GluGly	GluThr	GluTrp	GluSer	GluTyr	GluPro	GluHis	GluAsp	GluGlu	GluAsn	GluGln	GluLys	GluArg
Asn	AsnIle	AsnVal	AsnLeu	AsnPhe	AsnCys	AsnMet	AsnAla	AsnGly	AsnThr	AsnTrp	AsnSer	AsnTyr	AsnPro	AsnHis	AsnAsp	AsnGlu	AsnAsn	AsnGln	AsnLys	AsnArg
Gln	GlnIle	GlnVal	GlnLeu	GlnPhe	GlnCys	GlnMet	GlnAla	GlnGly	GlnThr	GlnTrp	GlnSer	GlnTyr	GlnPro	GlnHis	GlnAsp	GlnGlu	GlnAsn	GlnGln	GlnLys	GlnArg
Lys	LysIle	LysVal	LysLeu	LysPhe	LysCys	LysMet	LysAla	LysGly	LysThr	LysTrp	LysSer	LysTyr	LysPro	LysHis	LysAsp	LysGlu	LysAsn	LysGln	LysLys	LysArg
Arg	ArgIle	ArgVal	ArgLeu	ArgPhe	ArgCys	ArgMet	ArgAla	ArgGly	ArgThr	ArgTrp	ArgSer	ArgTyr	ArgPro	ArgHis	ArgAsp	ArgGlu	ArgAsn	ArgGln	ArgLys	ArgArg


 

方法1: 大人しくnumpy使う


正攻法 (numpy入ってる人にとっては)。

import numpy as np

Aminos = ["Ile", "Val", "Leu", "Phe", "Cys", "Met", "Ala", "Gly", "Thr", "Trp", "Ser", "Tyr", "Pro", "His", "Asp", "Glu", "Asn", "Gln", "Lys", "Arg"]

data = [[a1+a2 for a2 in Aminos] for a1 in Aminos]
with open("matrix.txt", "w") as f:
        f.write("\t" + "\t".join(Aminos) + "\n")
        np.savetxt(f, np.hstack([zip(Aminos), data]), fmt='%s', delimiter="\t")

 

方法2: とりあえずprintしてawkに投げる


awkに馴染みがあれば、まぁ楽といえば楽か

Aminos = ["Ile", "Val", "Leu", "Phe", "Cys", "Met", "Ala", "Gly", "Thr", "Trp", "Ser", "Tyr", "Pro", "His", "Asp", "Glu", "Asn", "Gln", "Lys", "Arg"]
print
for a1 in Aminos:
    print a1
for a1 in Aminos:
    print a1
    for a2 in Aminos:
        print a1+a2
% python matrix_test.py | awk 'BEGIN{c=1}{if(c%21 == 0){print $0}else{printf $0"\t"};c++}' > matrix.txt

方法3: printによる出力


見た目上は出来てるが実際は出来てない方法、とその解決方法。

Aminos = ["Ile", "Val", "Leu", "Phe", "Cys", "Met", "Ala", "Gly", "Thr", "Trp", "Ser", "Tyr", "Pro", "His", "Asp", "Glu", "Asn", "Gln", "Lys", "Arg"]

print "\t",
for a1 in Aminos:
    if Aminos[-1] == a1:
        print "%s\n" % a1,
    else:
        print "%s\t" % a1,
for a1 in Aminos:
    print "%s\t" % a1,
    for a2 in Aminos:
        if Aminos[-1] == a2:
            print "%s\n" % (a1+a2),
        else:
            print "%s\t" % (a1+a2),
問題点1

なぜ問題かは次のようにすれば分かる

for i in range(10):
    print "#",
% python print_test.py
# # # # # # # # # #

つまりprint,には改行コードの代わりに空白が入る。
これでは見かけは出来ていても再利用する際に空白とタブの混在でsplitが面倒になる。

これはPython3系であれば

for i in range(10):
    print("#", end="")

Python2系の場合は

import sys
for i in range(10):
    sys.stdout.write("#")

とすることで解消できる。

 

問題点2

今回の場合は出来ているが、listは重複を許すので

for a1 in Aminos:
    if Aminos[-1] == a1:
        print "%s\n" % a1,
    else:
        print "%s\t" % a1,

ではなく

for i in range(len(Aminos)):
    if len(Aminos) == i+1:
        print "%s\n" % Aminos[i],
    else:
        print "%s\t" % Aminos[i],

の方が確実かと。

方法4: 普通にwrite

Aminos = ["Ile", "Val", "Leu", "Phe", "Cys", "Met", "Ala", "Gly", "Thr", "Trp", "Ser", "Tyr", "Pro", "His", "Asp", "Glu", "Asn", "Gln", "Lys", "Arg"]

with open("matrix.txt", "w") as f:
    f.write("\t")
    for i in range(len(Aminos)):
        if len(Aminos) == i+1:
            f.write("%s\n" % Aminos[i])
        else:
            f.write("%s\t" % Aminos[i])
    for j in range(len(Aminos)):
        f.write("%s\t" % Aminos[j])
        for i in range(len(Aminos)):
            if len(Aminos) == i+1:
                f.write("%s\n" % (Aminos[j]+Aminos[i]))
            else:
                f.write("%s\t" % (Aminos[j]+Aminos[i]))

 

参考


How to print in Python without newline or space? - Stack Overflow

【Fedora 20】erlangのinstallに失敗する

% sudo yum install -y erlang

.
.
.

失敗:
  erlang-erts.x86_64 0:R16B-03.7.fc20                                                                   

完了しました!
% erl
zsh: command not found: erl

selinux-policyをupdateする必要があるらしい

sudo yum update -y selinux-policy


失敗したerlang-ertsだけ再インストール

sudo yum install -y erlang-erts

【JavaScript】地図上の任意の位置に円グラフを表示する

こんな感じで円グラフを表示する方法です。

http://www.cdc.gov/dhdsp/data_statistics/ems/images/mt_pers_type.jpg

http://www.cdc.gov/dhdsp/data_statistics/ems/fs_ems_mt.htm

作ってみて、とりあえず楽にカスタムできそうなやつが出来たのでコードをおいておきます。
結果としてはこんな感じ

jVectorMap and Raphaël - CodePen


Raphaëlは初めて使いましたがこれはいいですね、いじってて楽しいのでもう少し触ってみようかと思います。


 
コードなどは私のCodePenにあります。
今回は単純にこうできる、という紹介ですのであまりそれっぽくないですが...
外枠を太くしたり、といったことがg.Raphaelのpiechartに見た感じなさそうだったので大きさの同じcircleを作成してそれをアニメーションさせています。

あと、gridは座標が分かりやすいようつけているだけです。
 

使用ライブラリ, Version等

docker run ...のオプション指定が面倒になったのでコマンド作った

はじめに

user名とかを使いまわす人向けです。

最近dockerが楽しくて一日中弄ってます。

 
ただ、毎回思うのがオプション指定が多くて面倒

docker run -it -u cordea -w /home/cordea --name hoge hoge/hoge /usr/bin/zsh


 
/usr/bin/zsh に関してはDockerfileで指定すれば問題はない

ただ userworking directory はどうしたものやら
調べてもそもそも指定している人が少ない

というわけでpythonで簡単に書いてみました。

詳しいことはGitHub(下にリンクが貼ってあります)のREADME.mdを見て下さい。
 

使用方法

導入方法等はGitHubで。
ただ、調べればREADME.mdより詳しく書いてあるところがあるかと思います。

 
使用前

docker run -it -u cordea -w /home/cordea --name hoge hoge/hoge /usr/bin/zsh

使用後

dockerun hoge/hoge

 

もちろん以下のように一部のオプションだけ変更したり、もともとrunコマンドにあるオプションを使用するのも可能です。

dockerun -w / --name huge hoge/hoge

ただ、他のオプションを指定する場合はoptparseの仕様で"-- -a"のようにしないといけないみたいです。
まぁ、他のオプションを多用するならdockerunを書き換えるか、docker runを使うかするのが楽かと思いますが...


 
急ごしらえで作ったものでバグも多いかと思います。

バグを見つけたらご自身で直していただくか、ご連絡頂けますと幸いです。

 
探せば似たようなのはありそうだけどとりあえず満足。

GitHub


CORDEA/DockerFiles · GitHub

echo ${array[0]} ...それ大丈夫?

echo ${array[0]}

で出力される結果がshellによっては必ずしも望んだものにはならないという話


要するに配列のインデックスの開始がshellの種類によって0だったり1だったりするということです。
複数のshellを使う人には当たり前の知識かもしれませんが、私には新鮮だったのでメモ。

sh

sh-3.2$ array=(0 1 2 3)
sh-3.2$ echo ${array[*]}
0 1 2 3
sh-3.2$ echo ${array[0]}
0
sh-3.2$ echo ${array[1]}
1

bash

CORDEA@macrou:~$ array=(0 1 2 3)
CORDEA@macrou:~$ echo ${array[*]}
0 1 2 3
CORDEA@macrou:~$ echo ${array[0]}
0
CORDEA@macrou:~$ echo ${array[1]}
1

zsh

CORDEA@macrou% array=(0 1 2 3)
CORDEA@macrou% echo ${array[*]}
0 1 2 3
CORDEA@macrou% echo ${array[0]}

CORDEA@macrou% echo ${array[1]}
0

csh

[CORDEA@macrou ~]$ set array = ( 0 1 2 3 )
[CORDEA@macrou ~]$ echo ${array[*]}
0 1 2 3
[CORDEA@macrou ~]$ echo ${array[0]}

[CORDEA@macrou ~]$ echo ${array[1]}
0

tcsh

[CORDEA@macrou ~]$ set array = ( 0 1 2 3 )
[CORDEA@macrou ~]$ echo ${array[*]}
0 1 2 3
[CORDEA@macrou ~]$ echo ${array[0]}

[CORDEA@macrou ~]$ echo ${array[1]}
0

ksh

$ array=(0 1 2 3) 
$ echo ${array[*]}
0 1 2 3
$ echo ${array[0]}
0
$ echo ${array[1]}
1