Sponsored Link

月別アーカイブ: 11月 2016

(85) BeautifulSoupでなんちゃってGoogle検索

(85) BeautifulSoupでなんちゃってGoogle検索

先の「(79) なんちゃってGoogle検索」をBeautifulSoupを使って実装してみる。
だいぶ楽に実装できる。開発者様に感謝感謝 m(_ _)m

>>> import urllib.request
>>> from bs4 import BeautifulSoup
>>> 
>>> url="https://www.google.co.jp/search?q=応仁の乱"
>>> 
>>> opener = urllib.request.build_opener()
>>> opener.addheaders = [('User-agent', 'Mozilla/5.0')]
>>> html = opener.open(url)
>>> bs = BeautifulSoup(html.read())
>>> 
>>> for h3 in bs.findAll("h3",{"class":"r"}):
>>> 	print(h3.a.get_text())
>>> 	print(h3.a['href'])

実行結果は以下の通り。

応仁の乱 - Wikipedia
/url?q=https://ja.wikipedia.org/wiki/%25E5%25BF%259C%25E4%25BB%2581%25E3%2581%25AE%25E4%25B9%25B1&sa=U&ved=0ahUKEwjOi_Hd47bQAhUDFpQKHaJBAsYQFggUMAA&usg=AFQjCNFx2YRTF34Q90Pc-ZVBNiK2mHmjVQ
応仁の乱とは (オウニンノランとは) [単語記事] - ニコニコ大百科
/url?q=http://dic.nicovideo.jp/a/%25E5%25BF%259C%25E4%25BB%2581%25E3%2581%25AE%25E4%25B9%25B1&sa=U&ved=0ahUKEwjOi_Hd47bQAhUDFpQKHaJBAsYQFggeMAE&usg=AFQjCNEEgMYcO6EozSu66kBx92gM4eXlkg
応仁の乱について
/url?q=http://www12.plala.or.jp/rekisi/ounin.html&sa=U&ved=0ahUKEwjOi_Hd47bQAhUDFpQKHaJBAsYQFggjMAI&usg=AFQjCNG72yMzIbhr1SXfTdNzdDSx11AQcw
応仁の乱 | ニューワイド学習百科事典 | 学研キッズネット
/url?q=https://kids.gakken.co.jp/jiten/1/10020780.html&sa=U&ved=0ahUKEwjOi_Hd47bQAhUDFpQKHaJBAsYQFggpMAM&usg=AFQjCNG-egrj_Tu5iFSB_x2msSdsYRkH7Q
第32回 応仁の乱と衰退する室町幕府 - 歴史研究所 - 裏辺研究所
/url?q=http://www.uraken.net/rekishi/reki-jp32.html&sa=U&ved=0ahUKEwjOi_Hd47bQAhUDFpQKHaJBAsYQFgguMAQ&usg=AFQjCNGXknEN9pNpL94HdINbriS9AceYvA
応仁の乱(おうにんのらん)とは - コトバンク
/url?q=https://kotobank.jp/word/%25E5%25BF%259C%25E4%25BB%2581%25E3%2581%25AE%25E4%25B9%25B1-38826&sa=U&ved=0ahUKEwjOi_Hd47bQAhUDFpQKHaJBAsYQFgg0MAU&usg=AFQjCNFq5UqUG4vYc1n8DGBmyzqRKc6U9w
菊池寛 応仁の乱 - 青空文庫
/url?q=http://www.aozora.gr.jp/cards/000083/files/1368_37258.html&sa=U&ved=0ahUKEwjOi_Hd47bQAhUDFpQKHaJBAsYQFgg5MAY&usg=AFQjCNEVNvDd9tsTtAHOzs-t_VtCyU25bQ
応仁の乱について|社会の部屋|学習教材の部屋 - Biglobe
/url?q=http://www7a.biglobe.ne.jp/~gakusyuu/rekisi/ouninnoran.htm&sa=U&ved=0ahUKEwjOi_Hd47bQAhUDFpQKHaJBAsYQFgg_MAc&usg=AFQjCNH9aKPuRrhptgrhIPVqxLW8ZEUTDw
わかる歴史【室町時代】応仁の乱 前編 - YouTube
/url?q=https://www.youtube.com/watch%3Fv%3DObDWAbDMBEo&sa=U&ved=0ahUKEwjOi_Hd47bQAhUDFpQKHaJBAsYQtwIIRjAI&usg=AFQjCNG60Ikro_cShe86uEEQEYv1VXKcyA
応仁の乱が起こった理由 / 中学社会 歴史 by 早稲男 |マナペディア|
/url?q=http://manapedia.jp/text/798&sa=U&ved=0ahUKEwjOi_Hd47bQAhUDFpQKHaJBAsYQFghJMAk&usg=AFQjCNEB8cZKBua4-Gs8tYx7cfwZkDiz7A

(84) BeautifulSoupでスクレイピングを効率化

(84) BeautifulSoupでスクレイピングを効率化

先の「(81) 野球選手の成績を主成分分析 (続編1/2)」ではNPB様のホームページに記載されている個人打撃成績を取得させていただいた。前回は lxmlと XPathを使って個人成績データを抽出していたが、今回はこの処理を BeautifulSoup モジュールを使って行う。


(1) BeautifulSoupをインストール

$ pip3 install beautifulsoup4

(2) 2015年セリーグ打撃成績を取得してみる

① まずは情報を取得する。

>>> from urllib.request import urlopen
>>> from bs4 import BeautifulSoup
>>> html = urlopen("http://npb.jp/bis/2015/stats/bat_c.html")
>>> bs = BeautifulSoup(html.read())

② 次に、取得した情報から必要な情報だけを抽出する。

>>> ary_players = []
>>> for tr in bs.findAll("tr",{"class":"ststats"}):
>>>     ary_score = []
>>>     for td in tr.findAll("td"):
>>>         ary_score.append(td.get_text())
>>>     ary_players.append(ary_score)

or next_siblingsを使って順位列以降の必要なデータのみを抽出してもよい。

>>> ary_players = []
>>> for td in bs.findAll("tr",{"class":"ststats"}):
>>>     ary_score = []
>>>     for td in tr.td.next_siblings:
>>>         ary_score.append(td.get_text())
>>>     ary_players.append(ary_score)

③ 最後に結果を表示する。ここでCSVファイルなどに保存すればよい。

>>> for score in ary_players:
>>>     print(score)

取得した結果は以下の通り。

['1', '川端\u3000慎吾', '(ヤ)', '.336', '143', '632', '581', '87', '195', '34', '1', '8', '255', '57', '4', '3', '2', '2', '43', '0', '3', '72', '15', '.439', '.383']
['2', '山田\u3000哲人', '(ヤ)', '.329', '143', '646', '557', '119', '183', '39', '2', '38', '340', '100', '34', '4', '0', '3', '81', '1', '5', '111', '11', '.610', '.416']
['3', '筒香\u3000嘉智', '(デ)', '.317', '138', '568', '496', '79', '157', '28', '1', '24', '259', '93', '0', '0', '0', '2', '68', '0', '2', '98', '5', '.522', '.400']
['4', 'ルナ', '(中)', '.292', '134', '564', '496', '61', '145', '26', '1', '8', '197', '60', '11', '0', '0', '6', '57', '2', '5', '77', '13', '.397', '.367']
['5', 'ロペス', '(デ)', '.291', '140', '565', '516', '63', '150', '29', '1', '25', '256', '73', '1', '1', '0', '3', '44', '3', '2', '82', '14', '.496', '.347']
['6', '平田\u3000良介', '(中)', '.283', '130', '559', '491', '76', '139', '27', '3', '13', '211', '53', '11', '7', '1', '0', '64', '1', '3', '86', '5', '.430', '.369']
['7', '鳥谷\u3000敬', '(神)', '.281', '143', '646', '551', '69', '155', '21', '4', '6', '202', '42', '9', '6', '2', '3', '89', '2', '1', '77', '8', '.367', '.380']
['8', '福留\u3000孝介', '(神)', '.281', '140', '569', '495', '53', '139', '24', '3', '20', '229', '76', '1', '2', '1', '7', '65', '1', '1', '75', '15', '.463', '.361']
['9', 'マートン', '(神)', '.276', '140', '583', '544', '46', '150', '27', '0', '9', '204', '59', '0', '1', '0', '5', '31', '0', '3', '77', '21', '.375', '.316']
['10', '梶谷\u3000隆幸', '(デ)', '.275', '134', '578', '520', '70', '143', '35', '2', '13', '221', '66', '28', '13', '2', '2', '54', '0', '0', '132', '4', '.425', '.342']
['11', '新井\u3000貴浩', '(広)', '.275', '125', '480', '426', '52', '117', '22', '2', '7', '164', '57', '3', '0', '0', '4', '48', '1', '2', '73', '15', '.385', '.348']
['12', '田中\u3000広輔', '(広)', '.274', '141', '590', '543', '61', '149', '33', '9', '8', '224', '45', '6', '7', '5', '1', '34', '2', '7', '105', '8', '.413', '.325']
['13', 'ゴメス', '(神)', '.271', '143', '601', '520', '49', '141', '28', '0', '17', '220', '72', '0', '1', '0', '3', '72', '1', '6', '134', '15', '.423', '.364']
['14', 'エルナンデス', '(中)', '.271', '138', '548', '498', '54', '135', '27', '2', '11', '199', '58', '5', '3', '10', '4', '35', '1', '0', '106', '13', '.400', '.317']
['15', '雄平', '(ヤ)', '.270', '141', '585', '551', '57', '149', '33', '4', '8', '214', '60', '7', '4', '1', '3', '27', '1', '3', '82', '7', '.388', '.307']
['16', '坂本\u3000勇人', '(巨)', '.269', '130', '558', '479', '50', '129', '21', '3', '12', '192', '68', '10', '4', '9', '5', '65', '1', '0', '79', '5', '.401', '.353']
['17', '畠山\u3000和洋', '(ヤ)', '.268', '137', '584', '512', '64', '137', '26', '0', '26', '241', '105', '0', '0', '0', '8', '62', '0', '2', '92', '10', '.471', '.344']
['18', '大島\u3000洋平', '(中)', '.260', '142', '620', '565', '70', '147', '20', '4', '6', '193', '27', '22', '8', '10', '1', '39', '1', '5', '65', '5', '.342', '.313']
['19', 'バルディリス', '(デ)', '.258', '139', '525', '465', '38', '120', '23', '0', '13', '182', '56', '0', '0', '0', '5', '43', '2', '12', '62', '12', '.391', '.333']
['20', '菊池\u3000涼介', '(広)', '.254', '143', '644', '562', '62', '143', '20', '3', '8', '193', '32', '19', '9', '49', '2', '29', '2', '2', '92', '7', '.343', '.292']
['21', '上本\u3000博紀', '(神)', '.253', '108', '452', '375', '44', '95', '18', '1', '4', '127', '31', '19', '11', '29', '0', '44', '1', '4', '69', '1', '.339', '.338']
['22', '長野\u3000久義', '(巨)', '.251', '130', '479', '434', '49', '109', '20', '3', '15', '180', '52', '3', '2', '5', '2', '34', '0', '4', '81', '12', '.415', '.310']
['23', '丸\u3000佳浩', '(広)', '.249', '143', '633', '530', '81', '132', '28', '1', '19', '219', '63', '15', '7', '4', '4', '94', '2', '1', '143', '4', '.413', '.361']
['24', '中村\u3000悠平', '(ヤ)', '.231', '136', '502', '442', '36', '102', '14', '0', '2', '122', '33', '3', '2', '14', '2', '40', '1', '4', '80', '9', '.276', '.299']

(83) 「年ロボくん」を作る ver.1

(83) 「年ロボくん」を作る ver.1

東大合格を目指す「東ロボくん」が東大合格をいったん諦めた、とのニュースが流れていた。
現在の技術では自然言語処理が難しく、国語・英語の文章題を解けないのだとか。

そこまで本格的な物は作れないけれども、
・問題を与える
・WEB検索して回答を決める
というモノを作ってみたい。


テーマは年号当てロボット

いきなりハードルを上げず、まずはできそうなモノから作ってみる。
・[人] イベントに関するキーワードを与える。
・[COM] イベントの年号を回答する。(WEBスクレイピングにより)


動作仕様 ver.1

1)「(79) なんちゃってGoogle検索」の方法を使い、与えられたキーワードを Google様で検索させていただく。
2)検索結果として表示されたページを1個ずつ訪問し、「(81) 野球選手の成績を主成分分析 (続編1/2)」の方法を使って年号らしき表示物を抽出する。
3)抽出した年号でヒストグラムを作り、最頻値を回答として採用する。
※2)を改良し続けるのが大変な仕事なんだろうなぁ


実行結果

実行サンプルはこちら。
http://python.dogrow.net/sample/0083/
・プログラムが汚いので整理してからUPする。
・処理が重くてときどきタイムアウトしてしまう…







改善点

いろいろいろいろいろいろいろいろ…
Ver.2ではもうちょっと賢くしたい。

(82) 野球選手の成績を主成分分析 (続編2/2)

(82) 野球選手の成績を主成分分析 (続編2/2)

(81) 野球選手の成績を主成分分析 (続編1/2)」で作ったデータを使って、
(80) 野球選手を測ってみる 2016年版」と同じ解析をやってみる。
目的はオリジナルの物差しを作ること。


複数年度×複数リーグのデータを直列に繋ぐ

(81) 野球選手の成績を主成分分析 (続編1/2)」で作ったデータを直列に繋ぎ、一つの入力データにする。その際に、各行(=各選手)の先頭列に年度値を埋め込んでおく。後で「○○○○年の和田はすごかった!」みたいな喜び方をしたいから。

import csv
def load_data(basepath):
    files = glob.glob( basepath + '/*.csv')
    ary=[]
    for fpath in files:
        year = fpath.split('/')[-1].split('_')[0]
        f = open(fpath, 'r')
        dr = csv.reader(f)
        for row in dr:
            row.insert(0, year)
            ary.append(row)
    return np.array(ary)

12年間で延べ701人分の打撃結果を解析

12年間連続で規定打席に到達している人は1人で12人分カウントされる。

def calc_pca(X):
    Xm = np.broadcast_to(X.mean(axis=0), X.shape)
    Xs = np.broadcast_to(X.std(axis=0),  X.shape)
    Xv = (X - Xm) / Xs
    R1  = np.corrcoef(Xv.T)
    l,v = np.linalg.eigh(R1)
    r = l / l.sum()
    return v,r,Xv

def ctrl_proc():
    basepath = '../0081/data'
    ary_joined = load_data(basepath)
    # 不要項目を削除
    in_data = ary_joined[0::,6:24]
    # PCA
    X = in_data.astype(np.float64)
    v,r,Xv = calc_pca(X)
    return ary_joined,v,r,X,Xv

結果は?

(80) 野球選手を測ってみる 2016年版」と同じような物差しができた…
あんまりおもしろくないなぁ

第1主成分

よく試合に出場し、よくヒットを打ち、よくホームに帰り、長打も多く、打点も稼ぐ、スーパースターを見つけ出す物差し!

第2主成分

よく欠場するし、あんまりヒット打たないし、足遅くて併殺多いし、でもときどき一撃をくらわすパワーを持つ、ちょっとビミョーな準1流選手を見つけ出す物差し

12年間延べ701人の選手を物差しで測った結果は以下の通り。

(81) 野球選手の成績を主成分分析 (続編1/2)

(81) 野球選手の成績を主成分分析 (続編1/2)

(80) 野球選手を測ってみる 2016年版」では、日本プロ野球2016年シーズンの打者結果を使って遊んだ。どうせやるのならば、もっと大きなデータで同じことをやってみたい、プロ野球の過去の結果も使って同じ解析をしてみたい、と思う。

しかし…
前回はYahooスポーツの表示情報を手動でコピペして入力用CSVファイルを作成したが、この手作業を過去何年分もやると量が多すぎて途中で気持ちが切れちゃいそうだ。

そこで…
(75) 指定URLのWEBページのHTMLソースコードを表示」や
(79) なんちゃってGoogle検索
で使ったテクニックを利用して、過去データをプログラムに自動収集させてみたい。


過去データをスクレイピング

前述の(79)では、取得したHTMLの構文解析に HTMLParser モジュールを使用していた。
今回は楽をするために lxml モジュールを使う。C,C++での開発で libxmlを長いこと使っているが、これを使うと XPathでちょちょいと各タグにアクセスできるので便利だ。

まずは Pythonに lxmlをインストールしておく。

$ easy_install --user lxml
$ easy_install --user cssselect

データ収集&保存

NPB(日本野球機構)様のホームページでは、2005年~2016年の打者成績が掲載されているので、これをデータ解析の入力データとして使わせていただく。2005年~2016年の12年分×2リーグ分、全24ページのデータを使わせていただく。

index.cgi

#!/export/virtual/neuralnets/usr/local/bin/python
# -*- coding: utf-8 -*-

print 'Content-type: text/html'

import npbparser
import numpy as np

print """
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>0081</title>
</head>
<body>
"""

def collect_data(year, league_cp):
	url = "http://npb.jp/bis/%d/stats/bat_%s.html" % (year, league_cp)
	fname = "%d_%s.csv" % (year, league_cp)
	res = npbparser.get_npb_score(url);
	np.savetxt( fname, res, delimiter=',',fmt='%s')

for year in range(2005,2016):
	collect_data(year,'c')
	collect_data(year,'p')

print """
<form enctype="multipart/form-data" action="./" method=post>
<input type=submit value="実行">
</form>
</body>
</html>
"""

npbparser.py

#!~/usr/local/bin/python
# -*- coding: utf-8 -*-
import numpy as np
import urllib2
import lxml.html
import chardet

def get_npb_score(url):
    #------------------------------------------------
    # load html
    opener = urllib2.build_opener()
    opener.addheaders = [('User-agent', 'Mozilla/5.0')]
    res = opener.open(url)
    html = res.read()
    res.close()
    ccode = chardet.detect(html)          # 文字コードを判定
    html = html.decode(ccode['encoding']) # unicodeに変換

    #------------------------------------------------
    # parse HTML
    dom = lxml.html.fromstring(html)      # DOMツリー生成
    ary_tr = dom.xpath("//div[@id='stdivmaintbl']//table/tr")
    ary_1player = []
    for tr in ary_tr:
        if len(tr) < 25:
            continue
        da_items = []
        for td in tr:
            da_items.append(td.text.encode("utf-8"))
        ary_1player.append(da_items)
    ary_np = np.array(ary_1player)
    return ary_np

実行結果

過去12年分のCSVファイルデータができた。
※UTF-8なのでExcelで開くと文字化けするかも
2005年セ , 2005年パ
2006年セ , 2006年パ
2007年セ , 2007年パ
2008年セ , 2008年パ
2009年セ , 2009年パ
2010年セ , 2010年パ
2011年セ , 2011年パ
2012年セ , 2012年パ
2013年セ , 2013年パ
2014年セ , 2014年パ
2015年セ , 2015年パ
2016年セ , 2016年パ

次回はこのデータを使ってPCAしてみる。

(80) 野球選手を測ってみる 2016年版

(80) 野球選手を測ってみる 2016年版

姉妹サイト Octaveやってみる! で2014年に遊んでみた「(32) 野球選手の成績を主成分分析」と同じことをPythonでもやってみる。

対象データは 2016年のプロ野球セパ両リーグのレギュラークラス55人の打者成績とする。

データはYahoo!さんのサイト(↑)からお借りしました。m(_ _)m


テーマは野球選手の総合評価

個人的には、何でもそつなくこなす選手が好きです。
蓑田選手(阪急)
松永選手(阪急)
前田選手(広島)
などなど、本塁打、打点、打率などの単一項目での評価ではなく、
タイトルホルダーじゃないけどスゴイ選手!
の度合が計測できる、そんな物差しを見つけてくれることを楽しみにやってみます。


計算

主成分分析の対象とする変量(打者成績項目)は、以下の9項目とする。
打席数、安打、本塁打、塁打数、打点、得点、三振、四球、盗塁

前述の打者成績をCSVファイルに保存しておき、これをPythonでロードする。
※今回はscikit-learnを使わずにPCAしてみる。

>>> import numpy as np
>>> X = np.loadtxt('in_data.csv', delimiter=',')
>>> X
array([[ 576.,  168.,   23.,  271.,   75.,   96.,   67.,   81.,   13.],
       [ 528.,  156.,   29.,  285.,   95.,   76.,   79.,   53.,   16.],
       [ 561.,  151.,   44.,  319.,  110.,   89.,  105.,   87.,    0.],
       [ 640.,  181.,   13.,  248.,   56.,   92.,  106.,   40.,   13.],
       [ 523.,  141.,   11.,  205.,   59.,   52.,   78.,   61.,    0.],
       [ 590.,  146.,   38.,  292.,  102.,  102.,  101.,   97.,   30.],
       [ 576.,  160.,   25.,  267.,   81.,   58.,   83.,   38.,    1.],
       [ 458.,  127.,    1.,  154.,   32.,   48.,   31.,   34.,    3.],
       [ 513.,  136.,   19.,  220.,  101.,   66.,  101.,   54.,    0.],
       [ 607.,  155.,    0.,  179.,   39.,   74.,   66.,   63.,    7.],
       [ 566.,  157.,    1.,  183.,   38.,   38.,   98.,   22.,    2.],
       [ 656.,  175.,    3.,  229.,   27.,   80.,   69.,   46.,   26.],
       [ 652.,  162.,   20.,  268.,   90.,   98.,  107.,   84.,   23.],
       [ 522.,  131.,   11.,  191.,   49.,   80.,   93.,   38.,   19.],
       [ 618.,  163.,   11.,  232.,   42.,   58.,   78.,   33.,    8.],
       [ 530.,  136.,    8.,  193.,   65.,   48.,  109.,   27.,    5.],
       [ 471.,  114.,   22.,  202.,   68.,   63.,   68.,   44.,    1.],
       [ 450.,  108.,   18.,  190.,   56.,   69.,  110.,   49.,   26.],
       [ 537.,  123.,   31.,  236.,   96.,   64.,  116.,   72.,    0.],
       [ 679.,  154.,   13.,  216.,   39.,  102.,  119.,   77.,   28.],
       [ 518.,  127.,   34.,  258.,   95.,   66.,   75.,   24.,    0.],
       [ 465.,  109.,   24.,  205.,   68.,   48.,  106.,   39.,    0.],
       [ 554.,  127.,   22.,  213.,   79.,   58.,  130.,   48.,    2.],
       [ 507.,  116.,    6.,  165.,   46.,   38.,   69.,   27.,    1.],
       [ 494.,  103.,   14.,  171.,   73.,   61.,   89.,   72.,    4.],
       [ 533.,  106.,    7.,  145.,   36.,   49.,   80.,   75.,   13.],
       [ 458.,   81.,    4.,  107.,   35.,   27.,   76.,   36.,    2.],
       [ 607.,  178.,    8.,  242.,   69.,   74.,   64.,   68.,   12.],
       [ 593.,  155.,    5.,  196.,   43.,   76.,  113.,   73.,   41.],
       [ 611.,  172.,   24.,  284.,   82.,   73.,  108.,   38.,    8.],
       [ 616.,  163.,   17.,  240.,   70.,   79.,   84.,   75.,   53.],
       [ 536.,  131.,   18.,  224.,   73.,   82.,   97.,  100.,   23.],
       [ 605.,  169.,   18.,  242.,  106.,   62.,   53.,   38.,    3.],
       [ 671.,  171.,   11.,  244.,   62.,   98.,  103.,   77.,   18.],
       [ 555.,  145.,   14.,  213.,   61.,   66.,  121.,   42.,    5.],
       [ 612.,  140.,    7.,  184.,   50.,   69.,   53.,   99.,    6.],
       [ 583.,  143.,    6.,  195.,   61.,   62.,   56.,   50.,    3.],
       [ 513.,  129.,   20.,  214.,   76.,   56.,  105.,   47.,    5.],
       [ 570.,  139.,   24.,  238.,   92.,   81.,   89.,   64.,    0.],
       [ 569.,  133.,    3.,  176.,   41.,   52.,   87.,   83.,    0.],
       [ 481.,  118.,    7.,  173.,   40.,   56.,   95.,   30.,   11.],
       [ 497.,  116.,    2.,  141.,   43.,   39.,   49.,   61.,    1.],
       [ 488.,  110.,    1.,  130.,   34.,   51.,   53.,   42.,    6.],
       [ 626.,  147.,    2.,  169.,   53.,   61.,   56.,   69.,   22.],
       [ 520.,  122.,    1.,  143.,   33.,   64.,   69.,   40.,   53.],
       [ 589.,  137.,   27.,  247.,   88.,   74.,  123.,   58.,    2.],
       [ 449.,  108.,    0.,  131.,   46.,   27.,   44.,   19.,    1.],
       [ 610.,  142.,    2.,  178.,   33.,   63.,   55.,   56.,   16.],
       [ 598.,  144.,   39.,  282.,   97.,   71.,  138.,   44.,    0.],
       [ 609.,  142.,   27.,  256.,   85.,   79.,  141.,   48.,    6.],
       [ 583.,  129.,   35.,  260.,  103.,   73.,  148.,   59.,    1.],
       [ 485.,  106.,    6.,  145.,   35.,   44.,   49.,   46.,    7.],
       [ 624.,  142.,   25.,  245.,  110.,   61.,  126.,   47.,    2.],
       [ 590.,  122.,   10.,  184.,   56.,   74.,   86.,   47.,    8.],
       [ 600.,  115.,    0.,  127.,   28.,   66.,  117.,   63.,   23.]])

打者項目によって数値の範囲にばらつきがあるため、平均0、分散1に標準化しておく。
でも、項目によって価値がだいぶ違うものもあるので、項目別のレンジ調整が必要かも…

>>> Xm = np.broadcast_to(X.mean(axis=0), X.shape)
>>> Xs = np.broadcast_to(X.std(axis=0),  X.shape)
>>> Xv = (X - Xm) / Xs

固有ベクトル(v), 固有値(l)を求める。

>>> R1  = np.corrcoef(Xv.T)
>>> l,v = np.linalg.eigh(R1)

ランクが見やすいように寄与率を算出する。

>>> l / l.sum() * 100
array([  0.08933219,   1.01050674,   1.50297613,   2.19603783,
         6.06787656,   8.22801916,  11.12005857,  23.43222355,  46.35296927])

寄与率が大きいものは、
第1主成分 (8) 46.4%
第2主成分 (7) 23.4%
第3主成分 (6) 11.1%
ここまでで 80.9%

選んだ3種類の主成分スコアを算出する。

>>> scr_8  = Xv * np.broadcast_to(v[::,8:9].T, X.shape)
>>> scr_7  = Xv * np.broadcast_to(v[::,7:8].T, X.shape)
>>> scr_6  = Xv * np.broadcast_to(v[::,6:7].T, X.shape)

結果を見てみる

第1主成分から第3主成分まで、作ってくれた物差しは以下のようなもの。
※直観的に野球成績と比較しやすいように正負の向きを逆にした。

第1主成分 (46.4%)

ケガなく出場し、安打打ちまくり、デカいのも打て、よく本塁に帰ってくる。
いわゆるオールマイティな選手
↓を見ると、確かに誰もがスゴイと納得する選手が上位に並んでいる。

第2主成分 (23.4%)

出場機会は少ないが、本塁打をよく打ち打点を稼ぐ。でも早打ち&選球眼イマイチで鈍足。
いわゆる当たれば飛ぶ一発屋タイプ
↓を見ると、確かに一発屋の助っ人外国人選手が上位に並んでいる。

第3主成分 (11.1%)

非力であまりヒットを打たないが、よく四球を選んで出塁し、盗塁で相手守備をかき乱す。
いわゆる???
難しいなぁ、リードオフマンタイプでもないしなぁ、巨人鈴木選手タイプか?
寄与率11%だからこんなもんか…

全55人を各物差しで測った結果は以下の通り。


備忘録

リードオフマン物差しが出来なかったのが残念だ。
変量にリードオフマンの特徴を表すような項目を追加する必要がありそうだ。

(79) なんちゃってGoogle検索

(79) なんちゃってGoogle検索

Google検索&結果表示をPython経由で行う。
ウェブスクレイピングの基礎になるのかも(?)

実行サンプルはこちら。
http://python.dogrow.net/sample/0078/

(1/2) index.cgi

1ページ目しか表示できていない…
内部で start=10,20,30 などと切り替えて情報収集を繰り返せば複数ページ分を表示できるけど、まずは基本形のみを作ってみる。

#!/export/virtual/neuralnets/usr/local/bin/python
# -*- coding: utf-8 -*-

print 'Content-type: text/html'

import cgi
import myparser
import re

print """
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>サンプル</title>
</head>
<body>
"""

form = cgi.FieldStorage()
if form.has_key('word'):
	word = re.sub("[ | ]+","+",form['word'].value)
	url = 'https://www.google.co.jp/search?q=' + word
	linkItem = myparser.parse(url)
	for item in linkItem:
		title = item.get_title()
		site  = item.get_ref()
		if len(title) <= 0:
			title = '***'
		if re.match('http', site):
			href = site
		else:
			href = 'https://www.google.com' + site
		print '<a href=\"%s\">%s</a><hr />' % (href, title)

print """
<form enctype="multipart/form-data" action="./" method=post>
Google検索:<input type=text name=word style="width:400px">
<input type=submit value="実行">
</form>
</body>
</html>
"""

(2/2) myparser.py

強引なところもあるので、後で勉強し直して修正するかも…
指定WEBページのデータ取得には urllib2 モジュールのHTTPラッパー機能を使用する。
HTMLの構文解析には HTMLParser モジュールを使用する。(今回は楽できる lxml を使わない)
Pythonプログラムで文字列を処理する間は unicode で扱う。

#!~/usr/local/bin/python
# -*- coding: utf-8 -*-
import chardet
import urllib2
from HTMLParser import HTMLParser

class MyLinkItem:
    def __init__(self):
        self.m_ref = ''
        self.m_title = ''
    def set_ref(self, val):
        self.m_ref = val
    def set_title(self, val):
        self.m_title = val
    def get_ref(self):
        return self.m_ref.encode('utf-8')
    def get_title(self):
        return self.m_title.encode('utf-8')

class MyParser(HTMLParser):
    def __init__(self):
        HTMLParser.__init__(self)
        self.m_linkItem = []
        self.proc_atag = False
        self.title = ''
    def handle_starttag(self, tagname, attribute):
        if tagname.lower() == 'a':
            for i in attribute:
                if i[0].lower() == 'href':
                    if re.match('\/url\?q=http', i[1]):
                        if not re.search('webcache\.googleusercontent', i[1]):
                            self.proc_atag = True
                            self.title = ''
                            linkItem = MyLinkItem()
                            linkItem.set_ref(i[1])
                            self.m_linkItem.append(linkItem)
    def handle_endtag(self, tagname):
        if tagname.lower() == 'a':
            if self.proc_atag == True:
                self.m_linkItem[-1].set_title(self.title)
                self.proc_atag = False
    def handle_data(self, data):
        if self.proc_atag == True:
            self.title = self.title + data
    def get_linkItem(self):
        return self.m_linkItem

def parse(url):
    # load html
    opener = urllib2.build_opener()
    opener.addheaders = [('User-agent', 'Mozilla/5.0')]
    html = opener.open(url)
    data = html.read()
    html.close()
    rr = chardet.detect(data)           # 文字コードを判定
    data = data.decode(rr['encoding'])  # unicodeに変換
    # parse
    parser = MyParser()
    parser.feed(data)
    linkItem = parser.get_linkItem()
    parser.close()
    return linkItem

(77) subprocessでパイプライン処理を実現

(77) subprocessでパイプライン処理を実現

(37) subprocessで外部のプログラムを実行では、subprocessモジュールを使って外部プログラムを実行する方法を記した。しかし、この方法だと「複数コマンドをパイプで繋いだ場合にうまく動かない」のであった…

これは OK

cmd1 = "ls -lt"
sp.call(cmd1.split(" "))

これは NOK

cmd1 = "ls -lt | grep txt"
sp.call(cmd1.split(" "))

ではどうするか?

cmd1 = "ls -lt"
cmd2 = "grep txt"
res = sp.Popen(cmd1.split(" "), stdout=sp.PIPE)
sp.check_output(cmd2.split(" "), stdin=res.stdout)

または、

cmd = "ls -lt | grep txt"
res = sp.Popen(cmd, shell=True, stdout=sp.PIPE)
stdout,strerr = res.communicate()
print stdout
cmd = "ls -lt | grep txt"
res = sp.Popen(cmd, shell=True, stdout=sp.PIPE)
res.communicate()[0]

しかし、shell=True は任意のコマンドを自由自在に操られてしまう危険性がある。
しっかりとサニタイズされていない外部入力データを使うならば shell=True を使ってはダメ!

(76) ValueServerにPython2.7をインストール

(76) ValueServerにPython2.7をインストール

このブログは有料ホスティングサービスであるValueServer上に設置している。
ValueServerでは、標準インストールされているPythonのバージョンが 2.6.6 とちょっと古い。
(2016年11月現在)
このため、Python2.7以降で追加された新機能の一部が使用できない。

そこで、お借りしているローカルな環境に Python2.7 をインストールすることにした。
当然 root権限は与えられていないので…

(1) 最新のPythonインストーラを入手

2016年11月時点の最新版は Python2.7.11

$ cd
$ pwd
/virtual/xxxxxxxx
$ echo $HOME
/virtual/xxxxxxxx
$ wget https://www.python.org/ftp/python/2.7.11/Python-2.7.11.tgz

(2) インストール実行

ここでは、$HOME/usr/local 配下にPython2.7をインストールすることにした。

$ tar zxvf Python-2.7.11.tgz
$ cd Python-2.7.11
$ mkdir -p $HOME/usr/local/
$ CC=gcc CXX=g++ ./configure --prefix=$HOME/usr/local/
$ make
$ make install

(3) 環境変数の設定

自ユーザーでログインしたときには Python2.7が自動的に選択されるようにする。

$ cd
$ vi .bash_profile

以下を書き込む。
PYTHONHOME : 標準 Python ライブラリの場所(デフォルトでは /usr/local)
PYTHONPATH : モジュールファイルのデフォルトの検索パス(デフォルトはインストール依存)
※詳細はこちらを参照のこと。

export PATH=$HOME/usr/local/bin:$PATH
export PYTHONHOME=$HOME/usr/local
export PYTHONPATH=$HOME/usr/local/lib/python2.7/site-packages

書き込んだ内容を現shellに反映させる。

$ source .bash_profile

(4) Pythonのバージョンを確認

$ python --version
Python 2.7.11

これでインストールが完了!

後のパッケージインストールに備えて setuptools もインストールしておく。

$ wget https://bootstrap.pypa.io/ez_setup.py
$ python ez_setup.py

これで easy_install が使える。


ついでに Python3もインストール

Python3.5.2(2016年11月時点の最新版)もインストールしておく。

$ cd
$ pwd
/virtual/xxxxxxxx
$ echo $HOME
/virtual/xxxxxxxx
$ wget https://www.python.org/ftp/python/3.5.2/Python-3.5.2.tgz

$C=gcc CXX=g++ ./configure --prefix=$HOME/usr/local/

$make
$make install

$which python3
~/usr/local/bin/python3