プロクラシスト

今日の寄り道 明日の近道

Python/Javascriptを始めたい方に最適な『CheckIO』の遊び方!

こんにちは!ほけきよです。

データ分析業務では、プログラムを書くことが多いです。特に最近はpythonpythonは覚えやすく使いやすい初学者にはとてもいい言語だと思います。

しかし、それにも関わらず自分の実装力の無さに絶望する日々です。

どうやってコーディング力をつければ良いのか、相談した所、先輩に『CheckIO』というのを紹介してもらいました。今日はその紹介と、最初の遊び方について説明します!

  • 15秒でわかるCheckIO
  • 始め方
    • HPに行き、登録をする
    • 登録が済むと初期画面へ
  • 課題を始める
  • 課題を見てみる
  • 解き始める
    • def CheckIO(data):
    • assert
  • まとめ
続きを読む

【コードで一発】ブログ最適化/SEO対策で面倒なことは全てPythonにやらせよう

こんにちは、ほけきよです。

ここ数回に分けて書いた『面倒がすぎる内容をpythonにさせよう』シリーズの集大成。

ブログ最適化のために必要なものを『全て』『一気に』抜き出すプログラム、作りました!

この記事を読めば、下記の情報がゲットできます

※ 現在ははてなブログのみ対応となっています。WordPress用にも作成中なので、少々お待ちを。 *1

・記事とURLとブックマークの情報
・記事内画像を全て抜きとったもの
・自サイトの内部リンクがどうなっているかを可視化したもの
・リンク切れリスト
はてなブックマークがどのような伸び方でついたかを可視化したもの

使い方(情報技術に明るい人)

情報技術に明るい人と、そうでない人向けに使い方を分けて書きます。

githubからtaikutsu_blog_worksリポジトリをcloneしてきて、all_in_one.py実行してください。

git clone https://github.com/hokekiyoo/taikutsu_blog_works.git

requirement、引数等はREADMEに書いています。 *2 *3

この説明で???という方は次章からの説明をどうぞ

使い方(一般向け)

環境構築

MacLinuxユーザの方は、探せばいくらでもpython3の導入説明記事があると思います。

Windowsで動かしたい!という方には、私の過去記事を参考に環境構築してみてください。他にも方法はありますが、この方法が一番手っ取り早いと思います。(容量が2GBほど必要であることにご注意ください。)

実行するコード

下記コードをコピペしてall_in_one.pyなどで保存してください。

コードをみる
from argparse import ArgumentParser
from urllib import request 
from urllib import error
from bs4 import BeautifulSoup
import os
import csv
import json
import datetime
import time
import matplotlib.pyplot as plt

def extract_urls(args):
    page = 1
    is_articles = True
    urls = []
    while is_articles:
        try:
            html = request.urlopen("{}/archive?page={}".format(args.url, page))
        except error.HTTPError as e: 
            # HTTPレスポンスのステータスコードが404, 403, 401などの例外処理
            print(e.reason)
            break
        except error.URLError as e: 
            # アクセスしようとしたurlが無効なときの例外処理
            print(e.reason)
            break
        soup = BeautifulSoup(html, "html.parser")
        articles = soup.find_all("a", class_="entry-title-link")
        for article in articles:
            urls.append(article.get("href"))
        if len(articles) == 0:
            # articleがなくなったら終了
            is_articles = False
        page += 1
    return urls

def make_directories(args):
    directory = args.directory
    if not os.path.exists(directory):
        os.mkdir(directory)
    if args.image:
        if not os.path.exists(directory+"/imgs"):
            os.mkdir(directory+"/imgs")
    if args.graph:
        if not os.path.exists(directory+"/graph"):
            os.mkdir(directory+"/graph")
    if args.hatebu:
        if not os.path.exists(directory+"/hatebu"):
            os.mkdir(directory+"/hatebu")

def articles_to_img(args, url, soup, name):
    """
    各記事内の画像を保存
    - gif, jpg, jpeg, png
    - 記事ごとにフォルダ分けして保存される
    - imgs/{urlの最後の部分}/{0-99}.png
    """
    # ディレクトリの作成
    article_dir = os.path.join(args.directory+"/imgs", name)
    if not os.path.exists(article_dir):
        os.mkdir(article_dir)
    entry = soup.select(".entry-content")[0]
    imgs = entry.find_all("img")
    count=0
    
    
    for img in imgs:
        filename = img.get("src")
        if "ssl-images-amazon" in filename:
            # print("amazon img")
            continue
        # 拡張子チェック
        if filename[-4:] == ".jpg" or filename[-4:] == ".png" or filename[-4:] == ".gif":
            extension = filename[-4:]
            print("\t IMAGE:",filename)
        elif filename[-5:] == ".jpeg":
            extension = filename[-5:]
            print("\t IMAGE:",filename,extension)
        else: 
            continue
        try:
            image_file = request.urlopen(filename)
        except error.HTTPError as e: 
            print("\t HTTPERROR:", e.reason)
            continue
        except error.URLError as e: 
            print("\t URLERROR:", e.reson)
            continue
        # ValueErrorになった場合に試す(httpで始まらないリンクも貼れるっぽい?)
        except ValueError:
            http_file = "http:"+filename
            try: 
                image_file = request.urlopen(http_file)
            except error.HTTPError as e: 
                print("\t HTTPERROR:", e.reason)
                continue
            except error.URLError as e: 
                print("\t URLERROR:", e.reason)
                continue
        # 画像ファイルの保存
        with open(os.path.join(article_dir,str(count)+extension), "wb") as f:
            f.write(image_file.read())
        count+=1

def make_network(G, args, url, urls, soup):
    entry_url = args.url + "/entry/"
    article_name = url.replace(entry_url,"").replace("/","-")
    entry = soup.select(".entry-content")[0]
    links = entry.find_all("a")
    for link in links:
        l = link.get("href")
        if l in urls:
            linked_article_name = l.replace(entry_url,"").replace("/","-")
            print("\t NETWORK: 被リンク!{} -> {}".format(article_name, linked_article_name))
            j = urls.index(l)
            G.add_edge(article_name, linked_article_name)
        else: 
            continue

def url_checker(url, urls):
    #変なリンクは除去したい
    flag1 = "http" in url[:5]     
    #ハテナのキーワードのリンクはいらない
    flag2 = "d.hatena.ne.jp/keyword/" not in url    
    #amazonリンクはダメ
    flag3 = "www.amazon.co.jp" not in url and "http://amzn.to/" not in url
    #rakutenリンクはダメ
    flag4 = "rakuten.co.jp" not in url
    #もしももダメ
    flag5 = "af.moshimo" not in url
    return flag1 and flag2 and flag3 and flag4 and flag5


def check_invalid_link(args, urls, url, soup, writer):
    import re
    from urllib.parse import quote_plus
    regex = r'[^\x00-\x7F]' #正規表現    
    entry_url = args.url + "/entry/"
    entry = soup.select(".entry-content")[0]
    links = entry.find_all("a")
    for link in links:
        l = link.get("href")
        if l == None:
            continue
        #日本語リンクは変換
        matchedList = re.findall(regex,l)
        for m in matchedList:
            l = l.replace(m, quote_plus(m, encoding="utf-8"))
        check = url_checker(l, urls)
        if check:
            #リンク切れ検証
            try:
                html = request.urlopen(l)
            except error.HTTPError as e:
                print("\t HTTPError:", l, e.reason)
                if e.reason != "Forbidden":
                    writer.writerow([url,  e.reason, l])
            except error.URLError as e: 
                writer.writerow([url, e.reason, l])                        
                print("\t URLError:", l, e.reason)
            except TimeoutError as e:
                print("\t TimeoutError:",l, e)
            except UnicodeEncodeError as e:
                print("\t UnicodeEncodeError:", l, e.reason)

def get_timestamps(args, url, name):
    """
    はてブのタイムスタンプを取得
    """ 
    plt.figure()
    data = request.urlopen("http://b.hatena.ne.jp/entry/json/{}".format(url)).read().decode("utf-8")
    info = json.loads(data.strip('(').rstrip(')'), "r")
    timestamps = list()
    if info != None and "bookmarks" in info.keys(): # 公開ブックマークが存在する時に、それらの情報を抽出
        bookmarks=info["bookmarks"]
        title = info["title"]
        for bookmark in bookmarks:
            timestamp = datetime.datetime.strptime(bookmark["timestamp"],'%Y/%m/%d %H:%M:%S')
            timestamps.append(timestamp)
        timestamps = list(reversed(timestamps)) # ブックマークされた時間を保存しておく
    count = len(timestamps) 
    number = range(count)
    if(count!=0):
        first = timestamps[0]
        plt.plot(timestamps,number,"-o",lw=3,color="#444444")
        # 3時間で3
        plt.axvspan(first,first+datetime.timedelta(hours=3),alpha=0.1,color="blue")
        plt.plot([first,first+datetime.timedelta(days=2)],[3,3],"--",alpha=0.9,color="blue",label="new entry")
        # 12時間で15
        plt.axvspan(first+datetime.timedelta(hours=3),first+datetime.timedelta(hours=12),alpha=0.1,color="green")
        plt.plot([first,first+datetime.timedelta(days=2)],[15,15],"--",alpha=0.9, color="green",label="popular entry")
        # ホッテントリ
        plt.plot([first,first+datetime.timedelta(days=2)],[15,15],"--",alpha=0.7, color="red",label="hotentry")        
        plt.xlim(first,first+datetime.timedelta(days=2))
        plt.title(name)
        plt.xlabel("First Hatebu : {}".format(first))
        plt.legend(loc=4)
        plt.savefig(args.directory+"/hatebu/{}.png".format(name))
        plt.close()

def graph_visualize(G, args):
    import networkx as nx
    import numpy as np
    # グラフ形式を選択。ここではスプリングモデルでやってみる
    pos = nx.spring_layout(G)
    # グラフ描画。 オプションでノードのラベル付きにしている
    plt.figure()
    nx.draw(G, pos, with_labels=False, alpha=0.4,font_size=0.0,node_size=10) 
    plt.savefig(args.directory+"/graph/graph.png")
    nx.write_gml(G, args.directory+"/graph/graph.gml")
    # 次数分布描画
    plt.figure() 
    degree_sequence=sorted(nx.degree(G).values(),reverse=True) 
    dmax=max(degree_sequence) 
    dmin =min(degree_sequence)
    kukan=range(0,dmax+2) 
    hist, kukan=np.histogram(degree_sequence,kukan)
    plt.plot(hist,"o-")
    plt.xlabel('degree') 
    plt.ylabel('frequency')
    plt.grid()
    plt.savefig(args.directory+'/graph/degree_hist.png') 
    
def main():
    parser = ArgumentParser()
    parser.add_argument("-u", "--url", type=str, required=True,help="input your url")
    parser.add_argument("-d", "--directory", type=str, required=True,help="output directory")
    parser.add_argument("-i", "--image", action="store_true", default=False, help="extract image file from articles")
    parser.add_argument("-g", "--graph", action="store_true", default=False, help="visualize internal link network")
    parser.add_argument("-l", "--invalid_url", action="store_true", default=False, help="detect invalid links")
    parser.add_argument("-b", "--hatebu", action="store_true", default=False, help="visualize analyzed hatebu graph")   
    args = parser.parse_args()

    urls = extract_urls(args)
    # 保存用ディレクトリ作成
    make_directories(args)
    # 記事リストを作る
    with open (args.directory+"/articles_list.csv", "w") as f:
        writer = csv.writer(f, lineterminator='\n')
        writer.writerow(["Article TITLE", "URL","Hatebu COUNT"])
        if args.invalid_url:
            f = open(args.directory+'/invalid_url_list.csv', 'w')
            writer_invalid = csv.writer(f, lineterminator='\n')
            writer_invalid.writerow(["Article URL", "ERROR", "LINK"])        
        if args.graph:
            import networkx as nx
            G = nx.Graph()
            for i, url in enumerate(urls):
                name = url.replace(args.url+"/entry/","").replace("/","-")
                G.add_node(name)
        for i, url in enumerate(urls):
            name = url.replace(args.url+"/entry/","").replace("/","-")
            print("{}/{}".format(i+1,len(urls)), name)
            # 抽出したurlに対して各処理実行
            try:
                html = request.urlopen(url)
            except error.HTTPError as e: 
                print(e.reason)
            except error.URLError as e: 
                print(e.reason)
            soup = BeautifulSoup(html, "html.parser")
            # WordPressならいらない
            data = request.urlopen("http://b.hatena.ne.jp/entry/json/{}".format(url)).read().decode("utf-8")        
            info = json.loads(data.strip('(').rstrip(')'), "r")
            try:
                count = info["count"]
            except TypeError:
                count = 0
            # 記事の名前とurl、はてブを出力
            try:
                writer.writerow([soup.title.text, url, count])
            except UnicodeEncodeError as e:
                # ふざけた文字が入ってる場合はエラー吐くことも
                print(e.reason)
                print("\tArticleWriteWarning この記事のタイトルに良くない文字が入ってます :",url)
                continue
            if args.image:
                if "%" in name:
                    name = str(i) #日本語対応
                articles_to_img(args, url, soup, name)
            if args.graph:
                make_network(G, args, url, urls, soup)
            if args.invalid_url:
                check_invalid_link(args, urls, url, soup, writer_invalid)
            if args.hatebu:
                if "%" in name:
                    name = str(i) #日本語対応
                get_timestamps(args, url, name)
            time.sleep(3)
        if args.invalid_url:
            f.close()
        if args.graph:
            graph_visualize(G, args)


if __name__ == '__main__':
    main()

コマンドプロンプトやターミナルを動かしたことがない人は、デスクトップ上に保存しておくと、後々の説明でハマりづらいと思います。

または、githubページに飛び、下記画像に従いファイル一覧をDLしてください。

コマンド一発!実行する

コードの実行方法です。 コマンドプロンプト*4かターミナルを開き、all_in_one.pyを保存したフォルダまで行きます。デスクトップに保存している方は、下記コマンドを入力ください。

cd Desktop

デフォルトだとタイトル/URL/はてブcsvファイルだけですが、オプションの付け方でできることを選べるようにしています。詳しくはgithubページをお読みください。また、ここに使用上の注意点もかいていますので、一度読むと良いと思います。

全部使いたいときは、下記のように入力してください。

python all_in_one.py --url http://procrasist.com --directory procrasist --graph --image --hatebu --invalid_link

または

python all_in_one.py -u http://procrasist.com -d procrasist -g -i -b -l

この一発で、あとはきちんと動くはずです。(--url--directoryの後ろは自分が調べたいサイトURLを入れてください)

中身がどうなっているか

引数とできることをまとめたのがコチラ。必要ないなと思う情報は上記コマンドのオプションから消してください!

引数 意味
-u, –url 調べたいサイトのurl
-d, –directory 結果を保存するフォルダ名
-i, –image 記事内画像を抜き出す
-g, –graph 内部リンクの解析結果を表示する
-l, –invalid_url リンク切れを調べる
-b, –hatebu はてブの付き方の初動解析

なお、各要素技術はすでに過去記事にて説明しているので、気になる方はそちらをどうぞ

内部ネットワークについては、新たにグラフの次数分布を出せるようにしました(下記画像)。次数というのは、どのくらい他の記事と結びついているか(リンクの数)です。縦軸は記事数です。どの程度浮いた記事があるかを把握するのにお使いください。

この機能を応用すると、次のような可視化も可能です。yowaiが私です。。。強くしたい。。。

また、余談ですが、下記記事のsearch consoleの解析もpythonで行っています

pandasを使えばこういう表情報の抜き出しも簡単にできるので、興味のある方は是非。

注意

下記簡単に注意事項です。

  • リンク切れのチェックはまだ動作が不安定でエラーが出ることも多いかもしれません。
    • 楽天のリンクが多い人はチェックに時間がかかると思います。
  • リンクに対する日本語対応はまだ不完全です。リンクに日本語が含まれる場合は、フォルダ名等通し番号になります。

※注意事項追記(7/9)

  • 楽天アフィリエイト等はこのリンク切れ検査実行時に、クリック数をカウントしてしまうため、もしかするといつもより多くアクセスするために不正クリックとみなされる恐れがあります楽天等アフィリンクだけ別にアクセスするようコード修正予定ですが、現状使用の際は自己責任でお使いくださいませ。
  • 楽天/もしもからのリンクは取らないようにコードを修正しました。オプション付加は今後対応いたします。

出力結果

実行後は下記のフォルダ構成で出力されます。(procrasistで調べた場合)

├─all_in_one.py             実行するコード
└─procrasist                
    ├─articles.csv          記事一覧
    ├─invalid_url_list.csv  リンク切れ
    ├─graph                 内部ネットワーク
    ├─hatebu                はてブ解析
    └─imgs                  画像

また、出力ログは次のようにしています。

pythonを勉強したい方に

正直、スクールに行ったりするよりも、

  • 何をやりたいかを明確にする
  • 自分で手を動かしながら知り合いに聞きまくる

ほうが伸びると思います。

今回はブログ最適化という目的があったので、私は色々と勉強することができました。

正直Qiitaとかネット上に解説が溢れているので、適宜調べながらでOKだとは思います。 ただ、何に使うかという目的ベースから書かれているこの本は少し気になってます。

この記事はこの本タイトルにinspiredされて書いたものです。 時間があれば読んでみようかなと思っています。

私が書いてるコードは初心者なので未熟ですが、 単純なものの組み合わせでもこのくらいはできるようになるので、是非チャレンジしてみてください!

まとめ

ブログ最適化って、本当に大事なのはネットワークの構成を考えたり、記事内容をリライトしたり ってところですよね。それ以前の大変面倒な単純作業は人間がするようなところではありません。

頭の使わない退屈なことはpythonにやらせて、余った時間で有意義な最適化作業をしましょう。ではではっ!

いくつかのブログでチェックはしましたが、まだエラーが出ると思います。 エラー起きたり、こんな機能が欲しい!というのがあれば教えてね!機能は随時追加します!
準備は整った!!いよいよ最適化作業へとうつr( ˘ω˘ ) スヤァ…

*1: 7/10 WP版にも対応しました!下記リンクのコードをコピペし、all_in_one.pyをall_in_one_wp.pyとしてお使いください! WP用ツール

*2:コードが長くなっちゃったので分けようかと思ったのですが、記事にする都合全て一箇所にまとめています

*3:日曜大工程度なので、pull-requestやissue等お待ちしております!

*4:windowsボタン+cmd+Enterで開きます。

自サイトの記事のリンク切れを自動で抽出するアレを作った

こんにちは!ほけきよです。

今日は自動化第四弾!! またまたGoogleに媚びを売るSEO対策です(^^)

前回は内部リンクネットワークの可視化でした。今回は、リンク切れの抽出です!

悲しきNot FoundGoogleさんがお怒りになる前に片付けちゃいましょう!

リンク切れとは?

自サイトで言うと、こういうのです

悲しいですよね。以前まであったはずのものがなくなるのは…

というわけで、リンク切れは早めに対処しましょう

めんどい

そう思い、いちいちリンククリックして確かめてたんですが、

めんどい、めんどうがすぎる

大体のリンクはちゃんと通るのに、通らないリンクを探すなんて、修行ですか? というわけで、3記事で飽きました。ううーん、そうだ、自動化しよう

環境を作る

python環境のある人へ : pipで全部入ります。面倒な人は、anacondaに全部入っているはずなのでそれで。

python環境のないwinユーザへ : python環境をwindowsにいれる方法は、私の過去記事に書いています。anacondaを入れておくと大抵のことができるのではかどります(容量が2GBほど必要ということには注意しておいてください)

コード

例の如く下記コードをコピペしてinvalid_links.pyなどで保存してください *1

どこでもいいのですがあんまりわからない!って人はデスクトップに一旦保存しておきましょう。

コードをみる
from bs4 import BeautifulSoup
import urllib
from urllib import request
import time
from argparse import ArgumentParser
import csv


def extract_url(root_url):
    page = 1
    is_articles = True
    urls = []
    
    while is_articles:
        html = request.urlopen(root_url+"/archive?page={}".format(page))
        soup = BeautifulSoup(html, "html.parser")
        articles = soup.find_all("a",class_="entry-title-link")
        for article in articles:
            urls.append(article.get("href"))
        if len(articles) == 0:
            # articleがなくなったら終了
            is_articles = False
        page += 1
    return urls

def url_checker(url, urls):
    #変なリンクは除去したい
    flag1 = "http" in url[:5]     
    #ハテナのキーワードのリンクはいらない
    flag2 = "d.hatena.ne.jp/keyword/" not in url    
    #amazonリンクはダメ
    flag3 = "http://www.amazon.co.jp" not in url and "http://amzn.to/" not in url
    return flag1 and flag2 and flag3

def check_invalid_link(root_url, urls, output):
    import re
    regex = r'[^\x00-\x7F]' #正規表現    
    entry_url = root_url + "/entry/"
    with open (output, "w") as f:
        writer = csv.writer(f, lineterminator='\n')
        writer.writerow(["URL", "ERROR", "LINK", "STATUS"])
        for i,url in enumerate(urls):
            print(i+1,"/",len(urls),url)
            try:
                html = request.urlopen(url)
            except urllib.error.HTTPError as e: 
                print(e.reason)
            except urllib.error.URLError as e: 
                print(e.reason)
            soup = BeautifulSoup(html, "html.parser")
            entry = soup.select(".entry-content")[0]
            links = entry.find_all("a")
            for link in links:
                l = link.get("href")
                #日本語リンクは変換
                matchedList = re.findall(regex,l)
                for m in matchedList:
                    l = l.replace(m, urllib.parse.quote_plus(m, encoding="utf-8"))
                check = url_checker(l, urls)
                if check:
                    #リンク切れ検証
                    try:
                        html = request.urlopen(l)
                    except urllib.error.HTTPError as e: 
                        writer.writerow([url, "HTTP ERROR", l, e.reason])
                        print("HTTPError:", l, e.reason)
                    except urllib.error.URLError as e: 
                        writer.writerow([url, "URL ERROR", l, e.reason])                        
                        print("URLError:", l, e.reason)
                    except UnicodeEncodeError as e:
                        writer.writerow([url, "UNICODE ENCODE ERROR", l, e.reason])
                        print("UnicodeEncodeError:", l, e.reason)                        
            # time.sleep()

if __name__ == '__main__':
    """
    TODO
    - APIを叩いてなんとかなるやつ実装
        - amazon associateの対応
        - youtubeの削除された動画対応
    """
    parser = ArgumentParser()
    parser.add_argument("-u", "--url", type=str, required=True,help="input your url")
    parser.add_argument("-o", "--output", type=str, default="articles.csv", help="output csv name")
    args = parser.parse_args()
    urls = extract_url(args.url)
    check_invalid_link(args.url, urls, args.output)

なにをやっているかだけ簡単に説明しておきます。

  • 本文中からhttpで始まるリンクだけ抜き出している
  • ハテナのキーワードリンクは邪魔なので排除する
  • amazon associateリンクは特殊なことをしないとアクセスできないので除去する
  • リンクにアクセスして、エラーが出たら出力する

という流れになります。

実行する

ターミナル or コマンドプロンプトを開き*2、さっきのinvalid_links.pyが保存されている場所まで移動します。 デスクトップに保存している方は下記コマンドで実行されるはずです。

cd Desktop
python invalid_links.py --url http://www.procrasist.com --output procrasist.csv
  • --url URLで、調べたいブログのURLを入力してください
  • --output hoge.csv で、出力されるcsvの名前を決められます(省略できて、省略した場合はarticles.csvになります)

結果はこんな感じ。

最新記事から順番にびゃぁーーっと調べてくれます。

なお、結果はcsvファイルにも出力されます。 結構リンク切れってあるのね。

今後の課題/注意

下記のことはまだできていません

  • amazon商品リンクのリンク切れ
  • youtubeの削除されたものの追従
  • はてなブログのみに対応
  • ブラウザでのみアクセスを許しているものがあり、拒否(Forbidden)されちゃう。
  • 日本語以外のエンコードエラーに非対応(いずれ直します)

いずれもAPIでうまくすればうまくいくっぽいですが、ちょっと面倒だったので、今回はやめておきます。TODO! はてなブログ以外で試したいという方は、相談していただければ応えられる範囲で答えます!URLをぶっこ抜くところがおそらく一番のポイントです

あと、これは注意ですが、外部へのアクセスを頻繁に行いすぎると、そのサイト運営者の迷惑になりますので、コード実行は最小限にしておいてください

※コードが動かない場合も連絡ください。

まとめ

いかがでしたか? コレでいちいち眠たい眼こすりながらリンクポチポチする必要もなくなりましたねっ

どうぞ試してみてください!

よし、リンク切れ見つかったし、あとはリンクを直すだけd...( ˘ω˘)スヤァ

*1:Windowsのメモ帳でコピペする際は、デフォルトでShift-JISとかのふざけた文字コードになっている可能性があるので、UTF-8で保存しましょう。

*2:windowsユーザーへ。簡単な開き方は、windowsボタン+“cmdと入力”+Enterです

SEO対策!自分のブログの内部リンクを自動で可視化する

こんにちは!ほけきよです。

今日は自動化第三弾!! SEO対策ってしてますか? ブログの検索流入をあげるために、Googleには媚を売っていかなければいけないじゃないですか~。

そんなわけで、前回は表示スピード向上にむけた画像圧縮のために、全画像をぶっこ抜いてくる方法を紹介しました。

今日は、SEO対策で大事だと言われる。内部リンクのネットワーク化についてです。

内部リンクのネットワーク化

SEO ブログ

とかで調べるとわんさか出て来るわけですよ。内部リンクの密度、数、アンカーテキスト*1などが大事って。

なので、記事をほったらかしにするわけじゃなくて、過去記事にもリンクをはりながら、蜘蛛の巣のような構造にしていかなければいけないってわけです。

めんどい

というわけで、最適化しようと思うじゃないですか~。

めんどい、めんどうがすぎる。

まず、どの記事にリンクが貼られているかわからない。忘れた。 ううーん、面倒なものは自動化しましょう

環境を作る

  • python(3系)

    • bs4
    • urllib
    • networkx
    • numpy
    • matplotlib
  • python環境のある人へ : pipで全部入ります。面倒な人は、anacondaに全部入っているはずなので

  • python環境のないwinユーザへ : python環境をwindowsにいれる方法は、私の過去記事に書いています。anacondaを入れておくと大抵のことができるのではかどります(容量が2GBほど必要ということには注意しておいてください)

コード

下記コードをコピペしてlink_network.pyなどで保存してください。*2 *3

どこでもいいのですがあんまりわからない!って人はデスクトップに一旦保存しておきましょう。

コードをみる
from bs4 import BeautifulSoup
import urllib
from urllib import request
import time
import csv
import os
import numpy as np
import time
from argparse import ArgumentParser
import networkx as nx
import matplotlib.pyplot as plt

def extract_url(root_url):
    page = 1
    is_articles = True
    urls = []
    entry_url = root_url + "/entry/"
    
    while is_articles:
        html = request.urlopen(root_url+"/archive?page={}".format(page))
        soup = BeautifulSoup(html, "html.parser")
        articles = soup.find_all("a",class_="entry-title-link")
        for article in articles:
            urls.append(article.get("href"))
        if len(articles) == 0:
            # articleがなくなったら終了
            is_articles = False
        page += 1
    return urls

def make_network(root_url, urls):
    entry_url = root_url + "/entry/"
    G = nx.Graph()
    for url in urls:
        article_name= url.replace(entry_url,"").replace("/","-")
        G.add_node(article_name)
    for i,url in enumerate(urls):
        print(i+1,"/",len(urls))
        try:
            html = request.urlopen(url)
        except urllib.error.HTTPError as e: 
            print(e.reason)
        except urllib.error.URLError as e: 
            print(e.reason)
        soup = BeautifulSoup(html, "html.parser")
        entry = soup.select(".entry-content")[0]
        links = entry.find_all("a")
        for link in links:
            l = link.get("href")
            if l in urls:
                linking_article_name = url.replace(entry_url,"").replace("/","-")
                linked_article_name = l.replace(entry_url,"").replace("/","-")
                print("被リンク!{} -> {}".format(linking_article_name, linked_article_name))
                j = urls.index(l)
                G.add_edge(linking_article_name, linked_article_name)
            else: 
                continue
    return G

def visualize(G, savename, savegml):
    pos = nx.spring_layout(G) # グラフ形式を選択。ここではスプリングモデルでやってみる
    nx.draw(G, pos, with_labels=True,alpha=0.3,font_size=0.0,node_size=10) # グラフ描画。 オプションでノードのラベル付きにしている
    plt.savefig(savename+".png")
    plt.show()
    if savegml:
        nx.write_gml(G,savename+".gml")

    
if __name__ == '__main__':
    parser = ArgumentParser()
    parser.add_argument("-u", "--url", type=str, required=True,help="input your url")
    parser.add_argument("-o", "--savename", type=str, default="network", help="output name")
    parser.add_argument('-g', "--savegml", action="store_true", default=False)
    args = parser.parse_args()
    
    urls = extract_url(args.url)
    G = make_network(args.url, urls)
    visualize(G, args.savename, args.savegml)

何をしているかだけ軽く説明すると

■ URLをぶちぬいてリスト作る

■ 一記事ずつ本文からhrefついてるリンク抜き出す

■ リンクのURLがさっきのリストに含まれるものだったらグラフのエッジを張る

という手順でネットワークを作っていっています。リンクにはamazonリンクなど必要のないものも入っているので、予めURLリストを作っておくことがポイントですね。

使い方

ターミナル or コマンドプロンプトを開き*4、さっきのlink_network.pyが保存されている場所まで移動します。 デスクトップに保存している方は下記コマンドで実行されるはずです。

cd Desktop
python link_network.py --url http://www.procrasist.com --savename procrasist -g

一行目はデスクトップに移動しています。二行目の解説をちらっとしておくと

  • --url URLで、調べたいブログのURLを入力してください
  • --savename hoge で、出力されるグラフの名前を決められます(省略できて、省略した場合はgraph.pngになります)
  • -gは後で説明します。無くてもOKです。

結果は画面に描画されると同時に、pngファイルで保存されるようになっています!

出力結果

グラフとして出力されるのがこんな感じです。 ちなみに、比較用としてレコメンタンクを運営しているみるおか氏のリンク構造も載せておきます。

どちらが私でしょう?一目瞭然ですね。かなしい…(;_;) レコメンタンクの方は、かなり密なネットワーク構造になっていて、しっかり手入れをしているんだなと言うことがわかります。

どのようにしてそういう密なリンクを張っていくのか、下記記事でその一端に触れることができるので、最適化に興味がある人は是非みてみてはいかがでしょうか。

もう少し詳しく

このグラフ、出力でネットワークの粗密はわかるんですが、どうも、どういうカテゴリがクラスタを成しているかまではわかりづらいです。もう少し詳しくみたい!という方に、グラフ描画ソフトとの外部連携をおすすめします。

『使い方』で紹介した-gオプションで、savename.gmlというファイルが生成されるはずです。これは、グラフ構造を保ったファイルになっていて、これを使えば外部の描画ソフトと連携し、いい感じのグラフがかけるのです!! 有名所は

なんてのがあります。Gephiはなぜか私の環境じゃうまく動かなかったので、Cytoscapeを使います。

結果

グラフ可視化の結果がコチラ

よりスカスカグラフであることが判明しました!かなしい…(;_;)

このグラフは結構良くて、拡大するとどういう記事かまでわかるようになっています。

これは高校野球クラスタっぽいですね。じゃあどこと繋げられるかな~なんて考えることも可能です!

まとめ

いかがでしたか? こうやって見ると、強いブログ、弱いブログの差が一目瞭然ですね!

きちんとお手入れしなきゃなと痛感しました。皆さんも是非試してみてはいかがでしょうか?

よし、いよいよ最適化作業にうつr...( ˘ω˘)スヤァ

*1:リンクを貼る前の文言

*2:なお、今回は面倒だったのでエラーハンドリングしてません。必要な方は過去記事などを参考にどうぞ

*3:Windowsのメモ帳でコピペする際は、デフォルトでShift-JISとかのふざけた文字コードになっている可能性があるので、UTF-8で保存しましょう。

*4:windowsユーザーへ。簡単な開き方は、windowsボタン+“cmdと入力”+Enterです

はてなブログ記事内の画像を自動で全部ぶっこ抜く

こんにちは、ほけきよです。

ぶっこ抜き記事第二弾です。第一弾はこちら。今あるタイトルとURLを全部ぶっこ抜いてくるやつ

今回は記事最適化がメインです。なんでも

  • ページ表示スピードははSEOに大きく関係する

らしい。そして、

  • 画像の圧縮は表示スピードに大きく関係する!

らしい。つまり、画像を圧縮すればSEOに強くなるよ!ということ。画像を圧縮するために、まずは画像をぶっこ抜くことから始めよう。ということです。

画像の圧縮は表示スピードに大きく関係する!

ページ表示高速化に関しては、

つばさのーとさんの記事がめちゃくちゃ参考になります。

この中でも最もわかりやすいかつ効果的なのが画像の圧縮。これにより、表示スピードが格段に上がります。

いちいち画像をDLするのは面倒

なるほど!と思い、いちいち記事内画像を

右クリック⇨保存⇨Googleフォトで圧縮

としようと思っていたのですが、3記事で飽きました

やってられない。なので、これも自動化しちゃいましょう。

やりたいこと

まず、やりたいことを明確にしておきます

■ 記事内の画像(`gif`, `jpg`, `jpeg`, `png`)を抜きたい

■ 特に、**はてなフォトライフ**からの画像を全部抜きたい*1

■ ぶっこ抜いた画像は**Googleフォト**にアップして、画像をGoogleフォトからのリンクに差し替える

こんな感じ。

環境を作る

すでにpython環境を持っているという方は、bs4urllibだけ入れればOKです。*2 python環境をwindowsにいれる方法は、私の過去記事に書いています。anacondaを入れておくと大抵のことができるのではかどります(容量が2GBほど必要ということには注意しておいてください)

コード

下記コードをコピペしてimg_from_articles.pyなどで保存してください。

どこでもいいのですがあんまりわからない!って人はデスクトップに一旦保存しておきましょう。

コードをみる
#coding: utf-8

from bs4 import BeautifulSoup
import urllib
from urllib import request
import time
import csv
import os
from argparse import ArgumentParser

def extract_urls(root_url):
    """
    トップページを指定すると、ブログ内に存在するurlをすべて抜き出してくれる
    """
    is_articles = True
    page = 1
    urls = []
    while is_articles:
        try:
            html = request.urlopen("{}/archive?page={}".format(root_url, page))
        except urllib.error.HTTPError as e: 
            # HTTPレスポンスのステータスコードが404, 403, 401などの例外処理
            print(e.reason)
            break
        except urllib.error.URLError as e: 
            # アクセスしようとしたurlが無効なときの例外処理
            print(e.reason)
            break
        soup = BeautifulSoup(html, "html.parser")
        articles = soup.find_all("a",class_="entry-title-link")
        for article in articles:
            urls.append(article.get("href"))
        if len(articles) == 0:
            # articleがなくなったら終了
            is_articles = False
        page += 1
    return urls

def articles_to_img(root_url, urls):
    """
    各記事内の画像を保存
    - gif, jpg, jpeg, png
    - 記事ごとにフォルダ分けして保存される
    - imgs/{urlの最後の部分}/{0-99}.png
    """
    rootdir = "imgs2"
    if not os.path.exists(rootdir):
        os.mkdir(rootdir)
    for i, url in enumerate(urls):
        try:
            html = request.urlopen(url)
        except urllib.error.HTTPError as e: 
            print(e.reason)
        except urllib.error.URLError as e: 
            print(e.reason)
        soup = BeautifulSoup(html, "html.parser")
        # ディレクトリの作成
        dirname = url.replace(root_url+"/entry/","").replace("/","-")
        print(i, dirname)
        article_dir = os.path.join(rootdir, dirname)
        if not os.path.exists(article_dir):
            os.mkdir(article_dir)
        entry = soup.select(".entry-content")[0]
        imgs = entry.find_all("img")
        count=0
        for img in imgs:
            filename = img.get("src")
            # 拡張子チェック
            if filename[-4:] == ".jpg" or filename[-4:] == ".png" or filename[-4:] == ".gif":
                extension = filename[-4:]
                print("\t",filename)
            elif filename[-5:] == ".jpeg":
                extension = filename[-5:]
                print("\t",filename,extension)
            else: 
                continue
            try:
                image_file = request.urlopen(filename)
            except urllib.error.HTTPError as e: 
                print(e.reason)
                continue
            except urllib.error.URLError as e: 
                print("ERROR", e.reson)
                continue
            # 画像ファイルの保存
            with open(os.path.join(article_dir,str(count)+extension), "wb") as f:
                f.write(image_file.read())
            count+=1
        time.sleep(5)

if __name__ == '__main__':
    parser = ArgumentParser()
    parser.add_argument("-u", "--url", type=str, required=True,help="input your url")
    args = parser.parse_args()
    urls =  extract_urls(args.url)
    articles_to_img(args.url, urls)

使い方

ターミナル or コマンドプロンプトを開き*3、さっきのimg_from_articles.pyが保存されている場所まで移動します。 デスクトップに保存している方は下記コマンドで実行されるはずです。

cd Desktop
python img_from_articles.py --url http://www.procrasist.com

一行目はデスクトップに移動しています。二行目の解説をちらっとしておくと

  • --url 以後には自分のサイトのURLを入力してください。URLの最後の/はつけないでください。

これで、デスクトップにimgsというフォルダができて、その中に書く記事ごとにフォルダ分けされて画像が格納されているはずです。 こんな風に

f:id:imslotter:20170703023214p:plain

注意点は

  • フォルダ名はentry/以下の記事URLから抜いてきています
  • 日付が記事URLになっているやつは、"/"を"-"に変えています。
  • はてなフォトライフ以外からの画像は読み込めないときもあります*4
  • 実行時のアクセス負荷を考えて、各記事の間にsleep時間を入れている(5秒くらい)ので、少し時間がかかります。。

終わりに

いかがでしたか?

これでぶっこ抜いた画像をGoogleフォトに投げれば完璧です! めんどくさいことは機械的に終わらせたいですね。最近そんな本も出ていましたし。

よし、これで準備が整った!さぁ記事の最適化をするz...( ˘ω˘)スヤァ

*1:なんかpngで圧縮かけたときにうまくいかないことがあるらしい

*2:2系の人はurllib.requestをすべてurllib2に置き換えればいいはずです。面倒だったのでやっていません!いずれやるかも

*3:windowsユーザーへ。簡単な開き方は、windowsボタン+"cmdと入力"+Enterです

*4:そのときはエラーが出力されているはずです。

リライトのために、ブログの記事タイトルとURLを自動でぶっこ抜いてcsv化する

こんにちは、ほけきよです。

記事のリライトとか、最適化をしようとしたんですよ。 けど、どこをいつ最適化したとか、リライトしたとかわからなくなっちゃうんですよね。

じゃあ記事をエクセルで管理しちゃえば後々楽やん!天才。

と思って、一個一個温かみを持ってタイトルコピペとか繰り返してたんですが、 3回くらいで飽きました。 ムリ。

こんなんぶっこ抜くコード書いたほうが楽やん! という結論に至りましたので、書きました。

そして、意外に需要がありそうだったので、やっつけコードですが、紹介します。

なお、pythonで書いたのでpython環境があれば簡単に使えると思います。 注意点をいくつか書いているので用法用量をお守りの上、お使いください。

環境を作る

すでにpython環境を持っているという方は、bs4urllibだけ入れればOKです。*1 python環境をwindowsにいれる方法は、私の過去記事に書いています。anacondaを入れておくと大抵のことができるのではかどります(容量が2GBほど必要ということには注意しておいてください)

コード

下記コードをコピペしてarticles_to_csv.pyなどで保存してください。

どこでもいいのですがあんまりわからない!って人はデスクトップに一旦保存しておきましょう。

コードをみる
from bs4 import BeautifulSoup
import urllib
from urllib import request
import csv
from argparse import ArgumentParser


def articles_to_csv(url, output):
    is_articles = True
    page = 1
    with open (output, "w") as f:
        writer = csv.writer(f, lineterminator='\n') # 改行コード(\n)を指定しておく
        while is_articles:
            try:
                html = request.urlopen("{}/archive?page={}".format(url, page))
            except urllib.error.HTTPError as e: 
                # HTTPレスポンスのステータスコードが404, 403, 401などの例外処理
                print(e.reason)
                break
            except urllib.error.URLError as e: 
                # アクセスしようとしたurlが無効なときの例外処理
                print(e.reason)
                break
            soup = BeautifulSoup(html, "html.parser")
            articles = soup.find_all("a",class_="entry-title-link")
            for article in articles:
                try:
                    writer.writerow([article.text, article.get("href")])
                except UnicodeEncodeError as e:
                    # ふざけた文字が入ってる場合はエラー吐くことも
                    print(e.reason)
                    print("この記事のタイトルに良くない文字が入ってます :",article.get("href"))
            if len(articles) == 0:
                # articleがなくなったら終了
                is_articles = False
            page += 1
    

if __name__ == '__main__':
    parser = ArgumentParser()
    parser.add_argument("-u", "--url", type=str, required=True,help="input your url")
    parser.add_argument("-o", "--output", type=str, default="articles.csv", help="output csv name")
    args = parser.parse_args()
    articles_to_csv(args.url, args.output)

使い方

ターミナル or コマンドプロンプトを開き*2、さっきのarticles_to_csv.pyが保存されている場所まで移動します。 デスクトップに保存している方は下記コマンドで実行されるはずです。

cd Desktop
python articles_to_csv.py --url http://www.procrasist.com --output my_articles.csv

一行目はデスクトップに移動しています。二行目の解説をちらっとしておくと

  • --url 以後には自分のサイトのURLを入力してください。URLの最後の/はつけないでください。
  • --output 以後には保存したいファイル名を入力してください。入力しなくても良くて、その場合はarticles.csvという名前で保存されます。

これで、デスクトップにmy_articles.csvというcsvファイルが保存されたはずです。こんな感じ。

注意点

  • ファイル名に環境依存の文字が入っている場合は、csvに出力されません(例: ٩( ᐛ )و←こいつ / Pokémonのé など。)
  • csvに出力されない代わりに、この記事のタイトルに良くない文字が入っていますという適当なエラーメッセージをコンソール上に吐くようにしています。
  • csvと書いてますが、.xslxに拡張子を変えるとexcel専用になりますし、.csvのままでもexcelで開けます。
  • python等に詳しい人はgistにコードを挙げておいたので、そこから取ってきて適当に使ってください gist

  • TODO : 一回きりの利用を想定しているので、現行作業ファイルに新記事タイトルとURLの上書きなどの機能はない(いずれつけたい)

おわりに

これで一瞬でぶっこ抜くことができるようになりました。 リライトが捗るはず!!

使いたいっていう人で、なにかわからない所があればtwitter等で聞いてください。

リライト作業しよう、あ、その前にご飯食べてyoutubeみてカラオケいっt。。。

*1:2系の人はurllib.requestをすべてurllib2に置き換えればいいはずです。面倒だったのでやっていません!いずれやるかも

*2:windowsユーザーへ。簡単な開き方は、windowsボタン+“cmdと入力”+Enterです

研究者のブログ作業環境も紹介します!

[最終更新 : 2017.8.21]

こんにちは、ほけきよです。

作業環境を晒すことが流行っているみたいなので、私も流行りに乗って、作業環境を紹介してみます!

マシン環境

自作です、昨年作りました。こんな感じ

パーツ 製品 価格
OS windows10 12000
CPU intel corei56400k 19000
SSD シリコンパワー240GB 6500
メモリ 4GB×2 4000
マザボ asus h170 17000
電源 玄人志向のやつ 8000
筐体 Z9 5000

マシンは自作のものです。筐体全部入れて7万円程度。VM立ち上げたいしもうちょっとメモリ増やそうかな、あと欲を言えばGPUも欲しいよね。

エディタ

文章を書くにはマークダウン記法を使います。.md, html, cssなどは全部Visual Studio Code(VSCode)を使って編集します。 スニペットやリアルタイムプレビューなどが豊富で、基本的に文章もコードもコレを使っています。

VSCodeの魅力に関してはここにふんだんに書いていますので、どうぞ

表を作る環境

markdownで表を作るのって、結構面倒じゃないですか? そういう時に私が使うのがexcel+tablegeneratorです。 エクセルでぱぱっと表を作って、このtabelegeneratorにコピペするだけでmarkdown用の表に整形してくれます。

図を書く環境

Illustrator… ( ; ; )

パワポ職人なのでPowerpointで図を作ったりしています。 パワポの使い方はこちら

もうちょっとかっちょいい図を作りたいと思っているので、Inkscapeを使おうと思っています。無料版のイラレ

inkscape.org

写真の編集をする環境

Photoshop…( ; ; )

低収入なので、無料の画像編集ソフトGIMPを使っています。

GIMP - GNU Image Manipulation Program

操作性はそこまで良くないけど、素人がPhotoshopで編集したいことなどは、だいたいできるという感じです。操作性は良くないけど。

コーディング環境

ブログに書く程度の分析は、Windows上でぱぱっと動かせるだけで良かったりするので、windowsにanacondaをぶち込んでpythonを使っています。 エディタもjupyterを使ってブラウザ上でサクッと実装するという感じでやってます^^

python&jupyterをwindowsで使いたい人はこちらを参考にどうぞ。

進捗管理

Gitで管理してますが、実際単発の利用で終わることが多いので、あんまり頻繁に利用はしてないなって感じ。 また、ほけきよのGithubアカウントが欲しいなと思う今日この頃。 ⇒ 作りました!!

github.com

ほけきよの道具箱として、いろいろ作っていきます。いまのところあるのは

便利そうなものを思いついたら適宜更新していく感じです。issueやpull-reqは大歓迎です

複数のPCで文章を書きたい時。

たまにクラウド上にメモを残すことがあります。

私が使っているのがwripe` markdownでかけるのと、プレビューもできるところが好きなポイント。

また、ちょろっとしたコードはGistで書いて保存って感じです。

まとめ

みんなパソコン周りの環境 ばっかりだったので、内部の環境について紹介してみました!

パソコン周りはお金を積まないと難しいですが、内部環境はある程度無料で整えることができます。

是非是非使うときの参考にしてみてください。ではではっ!

AmazonがGoogleに対抗!翻訳サービスを開始する予定

f:id:imslotter:20170702113227p:plain

CNBCが報じているニュースによると、AmazonAWS上で翻訳サービスを開始するとのこと。

Amazon Web Services is launching machine translation service

この記事の意訳&まとめです

AWSとは

Amazon Web Serviceの略。 一般的に知られているわけではないがアマゾン*1クラウド上に様々なプラットフォームを用意している。

  • ストレージ
  • データベース
  • 分析基盤
  • 人工知能ツール

など、技術者にとって有用なサービスが並んでいる。

AWS人工知能サービス

AWSでの人工知能(Artificial Intelligence)を利用できるサービスは、近年爆発的に人気を-増していて、ストレージや計算資源に加えてAWSの収益の多様化に貢献している。なかでもDeeplearningを商用に利用したいと考えている企業も増えており、AWS Deep Learning AMIsを用いればDeeplearningの分析プラットフォームを手に入れられるようになっていて、画像認識や音声認識などに使われている。

Google翻訳サービス

その中でも、翻訳サービスはこの2~3年でのDeeplearning界隈でのホットなトピックである。 Googleが数ヶ月前にNeural Machine TranslationというDeeplearningを用いた機械翻訳技術を日本語に適用し、 今までにも増して高性能な翻訳を実現した。*2

Googleのほかにも、MicrosoftFacebookなどが翻訳サービスを持っている。

Amazon翻訳サービス

※翻訳に使うものがどういう技術かはイマイチわからなかった。

  • Amazonの翻訳サービスの拠点はSafabaというピッツバーグの会社。技術開発や研究を行っている。
  • すでに、企業内で複数の言語で製品情報を提供する際などに自社の機械翻訳技術を使用している。
  • AWSに載せて運用できるようにシステムを構築中

  • 2017年の3月に機械翻訳に関する報道発表が行われているlink

    • 翻訳に関するフィードバックをどううまく反映させるかのコンペが行われている
    • AWSサービスを使って良く、効率的にタスクをこなせるかを競う
    • 結果は9月の国際学会*3にて発表される
  • 実際のサービス発表は11月のAnnualレポートより前に行う見通し。

終わりに

どちらかとBtoBのサービスだと思うので、Google翻訳のように私たちに直接的にサービスを与えるようなものではないのかなぁとは思います。しかし、AWSを用いて運用している各国企業にとっては恩恵は大きいかもしれません。ここでの翻訳技術を用いることで、多くのグローバル企業で行われている翻訳作業の手間が省けるようになったり、新しいサービス創出につながるのではないのかなとおもっています。ではでは!

*1:厳密には別会社らしい

*2:NMTに関する技術はこちらのTutorialが詳しい(英語)

*3:http://www.statmt.org/wmt17/bandit-learning-task.html

【最新版】ポケモンしりとりの最長を、線形計画法で導き出す!

f:id:imslotter:20170702115124p:plain

こんにちは、ほけきよです。

数年前に、こんな素敵な論文が流行りましたね。

ポケモンつなげるもん♪

グラフ問題としてしりとりを定式化し、線形計画法で解く。といった一見普通の問題ですが、題材がポケモンであったことから、とある界隈では大きな反響を呼びました。

この時(2011年)はポケモンブラック&ホワイトだったので 全646匹, しりとりの最長は305という結果でした。

時は流れて2017年。色々と状況が変わってきました。

  • 線形計画法を解く、簡単なライブラリが増えてきた((pulp, scipy.linprogなど))
  • ポケモンが増えた(646⇨802)
  • 私の情報処理レベルがアップした

というわけで、 最新版!ポケモン繋げるもん♪やりましょう。

※結果を知りたい方は 3.ポケモンしりとりからどうぞ!

※今回は、結果を知りたいのがメインなので、巨人の肩に乗っかりまくりです。

線形計画問題とは

線形計画問題とは、目的関数と制約条件が1次式で表される最適化問題。 工場の生産管理なんかでよく使われたりします。 例として、Wikipediaに載っている問題を実際に値を入れながら解いてみましょうか。

農業を営む人が、小麦と大麦のための A 平方キロメートルの農地を持っている。農家は限度 F で肥料、限度 P で殺虫剤を使用することができる。これらはそれぞれ単位面積あたり小麦が (F_1, P_1) 、大麦が  (F_2,P_2) を必要とする。小麦の販売価格を  S_1、大麦の販売価格を S_2 、小麦を育てる領域を x_1、大麦を育てる領域を x_2 とすると、利益を最大化するためには、大麦と小麦をどれだけ育てればいいか。(育てる領域×販売価格をここでの利益と定義する)

なお、今回は
A = 10, F_1 = 2, F_2 = 3, F =40, P_1 = 4, P_2 =1, P =20, S_1 = 100, S_2 = 200
としてみます。

項目 大麦 小麦 条件 今回
育てる領域 x_1 x_2  x_1+x_2 \le A  x_1+x_2 \le 6
肥料の使用量 F_1x_1 F_2x_2 F_1x_1+F_2x_2\le F 2_1x_1+3_2x_2\le 15
殺虫剤の使用量 P_1x_1 P_2x_2 P_1x_1+P_2x_2 \le F 4x_1+1x_2\le 20
利益 S_1x_1 S_2x_2 最大化したい 100x_1+200x_2

なにやら複雑ですね。こうやって様々な要素が絡み合った問題を解くのが線形計画問題です。

可視化するとこんな感じ。 ここの赤で囲まれたところを

f:id:imslotter:20170624103207p:plain

線形計画法ライブラリ「pulp

解くのには、それなりの知識や、早く解くためには専門的な知識が必要なのですが、 軽く作った問題を解きたい!!という時には、ライブラリを使うのが今の時代なら良いでしょう。

ここでは、pythonpulpというライブラリを使います。 pulp公式ドキュメントが割と貧弱なので、先人のQiita記事がとても参考になり助かります。

PuLP による線型計画問題の解き方ことはじめ - Qiita

インストール

pip install pulp

でOKです。

さっきの問題をとく

記事を見ながら、さっきの問題を解きましょう。 ポンポンっと条件を入れていけば、答えを出してくれます。

#coding:  utf-8
import pulp

problem = pulp.LpProblem('sample', pulp.LpMaximize)
# 変数の設定
x1 = pulp.LpVariable('x1', 0)
x2 = pulp.LpVariable('x2', 0)
# 最適化したいものの設定
problem += 100*x1 + 100*x2
# 制約条件の設定
problem += x1 + x2 <= 6
problem += 2*x1 + 3*x2 <= 15
problem += 4*x1 + x2 <= 20

# 結果
print ("Result : (x1, x2) = ({},{})".format(x1.value(), x2.value()))
>>> Result : (x1, x2) = (3.0,3.0)

これは便利ですねぇ。

ポケモンしりとり

では、これを使ってポケモンしりとりを解いていきましょう。

こちらも先人様のサイトを参考に。

しりとりを線形計画問題として定式化します。

条件式 意味
最適化対象 \sum_i \sum_j x_{ij} なるべく多くポケモンをつなげる
変数 x_{ij} \in \left\{0,1\right\} \forall i,j しりとりでのキーワードの
つながりを示す(i,jはポケモン図鑑の番号みたいなもの)
y_{i} \in \left\{0,1\right\} \forall i i番目がしりとりの先頭かどうか
z_{i} \in \left\{0,1\right\} \forall i i番目はしりとりでは何番目か
条件  \sum_i x_{ij} = 1 \forall i 同じ単語は使わない(入力制約)
\sum_j x_{ji} =1 \forall i 同じ単語は使わない(出力制約)
\sum_j x_{ij} \le \sum_j x_{ij} + y_i  \forall i yに関する制約
z_i \le z_j -(n+1)\times (1-x_{ij}) \forall i,j zに関する制約
\sum_i y_i = 1 先頭は1つだけ

ポケモンデータを打ち込む用意

いちいち入力するのは面倒なので、wikipediaからぶっこぬいて使いましょう。 ポケモンデータも、以下の前処理をしておきます

こうすると"ポリゴン2"⇨"ホリコンツ"となります。ぶっこ抜くコードはこちら(python3系)

# スクレイピング
from bs4 import BeautifulSoup
from urllib import request

pokemons = []
poke_num = 802
# スクレイピング
html = request.urlopen("https://ja.wikipedia.org/wiki/%E5%85%A8%E5%9B%BD%E3%83%9D%E3%82%B1%E3%83%A2%E3%83%B3%E5%9B%B3%E9%91%91%E9%A0%86%E3%81%AE%E3%83%9D%E3%82%B1%E3%83%A2%E3%83%B3%E4%B8%80%E8%A6%A7")
soup = BeautifulSoup(html, "html5lib")
tables = soup.find_all("td")
for table in tables:
    pokes = table.find_all("td")
    for i,poke in enumerate(pokes):
        if i%2 == 1:
            if poke.string != '\xa0':
                pokemons.append(poke.string)
pokemons = pokemons[:poke_num]
kw = [pokemon for pokemon in pokemons]
# 処理1 記号を日本語に
kw[28] = "ニドランメス"
kw[31] = "ニドランオス"
kw[232] = "ポリゴンツー"
kw[473] = "ポリゴンゼット"
# 処理2 濁音、拗音を処理、"ー"を除去
d = {i:j for i, j in zip('ガギグゲゴザジズゼゾダヂヅデドバビブベボパピプペポィャュョ',
                         'カキクケコサシスセソタチツテトハヒフヘホハヒフヘホイヤユヨ')}
kw = [''.join(d.get(c, c) for c in s.rstrip('ー')) for s in kw]

結果 しりとり。

これkwとして、を先ほどのQiita記事線形計画法に入れてやりましょう。(最後の出力部分のところだけkwpokemonsに変えれば、元のポケモン名になります。)

151匹

ポケモン初期なら何個繋がるのか。答えは61でした!ベロリンガから始まり、タッツーから終わる。

[1]ベロリンガ -> [2]カイリキー -> [3]キャタピー -> [4]ピッピ -> [5]ビリリダマ -> [6]マンキー -> [7]ギャロップ -> [8]フシギバナ -> [9]ナッシー -> [10]シードラ -> [11]ラフレシア -> [12]アーボック -> [13]クラブ -> [14]ブーバー -> [15]バタフリー -> [16]リザード -> [17]ドククラゲ -> [18]ケーシィ -> [19]イーブイ -> [20]イワーク -> [21]クサイハナ -> [22]ナゾノクサ -> [23]サワムラー -> [24]ライチュウ -> [25]ウツボット -> [26]ドガース -> [27]ズバット -> [28]ドードー -> [29]ドードリオ -> [30]オニスズメ -> [31]メノクラゲ -> [32]ゲンガー -> [33]カモネギ -> [34]キングラー -> [35]ラッタ -> [36]ダグトリオ -> [37]オムナイト -> [38]トサキント -> [39]トランセル -> [40]ルージュラ -> [41]ラッキー -> [42]ギャラドス -> [43]スリーパー -> [44]ハクリュー -> [45]ユンゲラー -> [46]ラプラス -> [47]スリープ -> [48]フリーザー -> [49]サンダース -> [50]スピアー -> [51]アーボ -> [52]ポッポ -> [53]ポニータ -> [54]タマタマ -> [55]マタドガス -> [56]スターミー -> [57]ミュウ -> [58]ウインディ -> [59]イシツブテ -> [60]ディグダ -> [61]タッツー

最新版、ポケモンつなげるもん♪

さあ、いよいよ最新版の802匹でしりとり検索です!! 結果は…

387

でした!

そして、これが最長のしりとり結果です。

[1]エレブー -> [2]ブロスター -> [3]タネボー -> [4]ポッチャマ -> [5]マラカッチ -> [6]チョボマキ -> [7]キングラー -> [8]ラグラージ -> [9]ジャランゴ -> [10]コフキムシ -> [11]シェイミ -> [12]ミュウツー -> [13]ツタージャ -> [14]ヤヤコマ -> [15]マーイーカ -> [16]カプ・テテフ -> [17]フシデ -> [18]ディアンシー -> [19]ジャノビー -> [20]ヒトカゲ -> [21]ゲッコウガ -> [22]カイリュー -> [23]ユクシー -> [24]ジャラコ -> [25]ゴルーグ -> [26]クラブ -> [27]ファイヤー -> [28]ヤルキモノ -> [29]ノコッチ -> [30]チョンチー -> [31]チェリンボ -> [32]ポッポ -> [33]ホエルコ -> [34]ゴマゾウ -> [35]ウツボット -> [36]ドッコラー -> [37]ラティアス -> [38]スカンプー -> [39]フラエッテ -> [40]テッポウオ -> [41]オニシズクモ -> [42]モジャンボ -> [43]ポカブ -> [44]ブラッキー -> [45]キバニア -> [46]アーボック -> [47]グソクムシャ -> [48]ヤミラミ -> [49]ミカルゲ -> [50]ケルディオ -> [51]オーロット -> [52]トリデプス -> [53]スワンナ -> [54]ナマケロ -> [55]ロゼリア -> [56]アーマルド -> [57]ドククラゲ -> [58]ケララッパ -> [59]ハネッコ -> [60]ゴニョニョ -> [61]ヨワシ -> [62]シママ -> [63]マメパト -> [64]トドゼルガ -> [65]ガラガラ -> [66]ライチュウ -> [67]ウリムー -> [68]ムクバード -> [69]トゲチック -> [70]クヌギダマ -> [71]マナフィ -> [72]イトマル -> [73]ルージュラ -> [74]ランクルス -> [75]ストライク -> [76]グライガー -> [77]カプ・レヒレ -> [78]レックウザ -> [79]サメハダー -> [80]タッツー -> [81]ツボツボ -> [82]ポポッコ -> [83]コジョフー -> [84]フシギダネ -> [85]ネマシュ -> [86]ユキワラシ -> [87]シードラ -> [88]ライボルト -> [89]ドンメル -> [90]ルナアーラ -> [91]ラランテス -> [92]ズバット -> [93]ドレディア -> [94]アシマリ -> [95]リオル -> [96]ルリリ -> [97]リザード -> [98]ドクロッグ -> [99]クサイハナ -> [100]ナッシー -> [101]ジラーチ -> [102]チルット -> [103]ドヒドイデ -> [104]テッカグヤ -> [105]ヤンヤンマ -> [106]マッギョ -> [107]ヨルノズク -> [108]グレイシア -> [109]アママイコ -> [110]コロトック -> [111]クルミル -> [112]ルンパッパ -> [113]バケッチャ -> [114]ヤナップ -> [115]ブーバー -> [116]バニリッチ -> [117]チュリネ -> [118]ネクロズマ -> [119]マネネ -> [120]ネイティ -> [121]イルミーゼ -> [122]ゼニガメ -> [123]メノクラゲ -> [124]ゲンガー -> [125]カラナクシ -> [126]シェルダー -> [127]ダゲキ -> [128]キリンリキ -> [129]キテルグマ -> [130]マダツボミ -> [131]ミミッキュ -> [132]ユンゲラー -> [133]ランドロス -> [134]スピアー -> [135]アリゲイツ -> [136]ツンベアー -> [137]アマージョ -> [138]ヨーテリー -> [139]リグレー -> [140]レジロック -> [141]グラエナ -> [142]ナゾノクサ -> [143]サニーゴ -> [144]ゴルバット -> [145]トルネロス -> [146]スボミー -> [147]ミツハニー -> [148]ニンフィア -> [149]アズマオウ -> [150]ウソッキー -> [151]キュレム -> [152]ムウマ -> [153]マクノシタ -> [154]ダグトリオ -> [155]オノンド -> [156]ドゴーム -> [157]ムチュール -> [158]ルチャブル -> [159]ルカリオ -> [160]オムスター -> [161]ダーテング -> [162]クレベース -> [163]スターミー -> [164]ミネズミ -> [165]ミミロップ -> [166]フリージオ -> [167]オタチ -> [168]チェリム -> [169]ムーランド -> [170]ドテッコツ -> [171]ツツケラ -> [172]ラブカス -> [173]スカタンク -> [174]クルマユ -> [175]ユキノオー -> [176]オンバット -> [177]トゲピー -> [178]ヒドイデ -> [179]ディグダ -> [180]タブンネ -> [181]ネオラント -> [182]ドダイトス -> [183]ズルッグ -> [184]クレッフィ -> [185]イベルタル -> [186]ルクシオ -> [187]オニゴーリ -> [188]リングマ -> [189]マグマッグ -> [190]クズモー -> [191]モンジャラ -> [192]ラフレシア -> [193]アメタマ -> [194]ママンボウ -> [195]ウツロイド -> [196]ドラミドロ -> [197]ロトム -> [198]ムシャーナ -> [199]ナットレイ -> [200]イーブイ -> [201]イノムー -> [202]ムンナ -> [203]ナゲキ -> [204]キバゴ -> [205]コアルヒー -> [206]ビッパ -> [207]バルキー -> [208]キノココ -> [209]ゴローニャ -> [210]ヤナッキー -> [211]ギラティナ -> [212]ナックラー -> [213]ラッタ -> [214]ダイケンキ -> [215]キャモメ -> [216]メリープ -> [217]フワンテ -> [218]デデンネ -> [219]ネッコアラ -> [220]ランプラー -> [221]ライコウ -> [222]ウソハチ -> [223]チャーレム -> [224]ムックル -> [225]ルギア -> [226]アギルダー -> [227]タマンタ -> [228]ダークライ -> [229]イワパレス -> [230]スリープ -> [231]プロトーガ -> [232]カミツルギ -> [233]キャタピー -> [234]ビクティニ -> [235]ニャスパー -> [236]ハーデリア -> [237]アマカジ -> [238]シュバルゴ -> [239]コソクムシ -> [240]シシコ -> [241]ゴーリキー -> [242]キュワワー -> [243]ワタッコ -> [244]コロボーシ -> [245]ジュナイパー -> [246]バオップ -> [247]フラベベ -> [248]ペリッパー -> [249]バネブー -> [250]フーパ -> [251]バニプッチ -> [252]チコリータ -> [253]ダルマッカ -> [254]カエンジシ -> [255]ジグザグマ -> [256]マンキー -> [257]ギャロップ -> [258]フィオネ -> [259]ネイティオ -> [260]オニスズメ -> [261]メガヤンマ -> [262]マケンカニ -> [263]ニョロゾ -> [264]ゾロア -> [265]アブリー -> [266]リリーラ -> [267]ラクライ -> [268]イシズマイ -> [269]イシツブテ -> [270]ディアルガ -> [271]ガマゲロゲ -> [272]ケムッソ -> [273]ソルガレオ -> [274]オオタチ -> [275]チャオブー -> [276]フシギソウ -> [277]ウパー -> [278]ハトーボー -> [279]ボクレー -> [280]レディバ -> [281]ハリテヤマ -> [282]マグカルゴ -> [283]コジョンド -> [284]トゲキッス -> [285]スバメ -> [286]メブキジカ -> [287]カイオーガ -> [288]カメテテ -> [289]デンリュウ -> [290]ウデッポウ -> [291]ウインディ -> [292]イワンコ -> [293]ゴンベ -> [294]ヘイガニ -> [295]ニャルマー -> [296]マニューラ -> [297]ラッキー -> [298]キモリ -> [299]リーフィア -> [300]アチャモ -> [301]モグリュー -> [302]ユキメノコ -> [303]ゴロンダ -> [304]タマザラシ -> [305]ジヘッド -> [306]ドロバンコ -> [307]コスモッグ -> [308]クレセリア -> [309]アゴジムシ -> [310]シズクモ -> [311]モココ -> [312]ゴビット -> [313]ドードー -> [314]トサキント -> [315]ドジョッチ -> [316]チョロネコ -> [317]コスモウム -> [318]ムウマージ -> [319]ジガルデ -> [320]デンヂムシ -> [321]ジャラランガ -> [322]カプ・コケコ -> [323]コラッタ -> [324]ダンゴロ -> [325]ロズレイド -> [326]トドグラー -> [327]ラティオス -> [328]ズガイドス -> [329]スリーパー -> [330]ハスボー -> [331]ホルビー -> [332]ヒヤップ -> [333]フォッコ -> [334]ゴチム -> [335]ムクホーク -> [336]クロバット -> [337]ドデカバシ -> [338]シロデスナ -> [339]ナマコブシ -> [340]シュシュプ -> [341]フリーザー -> [342]サボネア -> [343]アシレーヌ -> [344]ヌイコグマ -> [345]マグマラシ -> [346]シザリガー -> [347]カラカラ -> [348]ラムパルド -> [349]ドーミラー -> [350]ラルトス -> [351]スコルピ -> [352]ビブラーバ -> [353]バスラオ -> [354]オドシシ -> [355]ジュペッタ -> [356]タツベイ -> [357]イワーク -> [358]クチート -> [359]ドードリオ -> [360]オオスバメ -> [361]メラルバ -> [362]ハヤシガメ -> [363]メレシー -> [364]シキジカ -> [365]カモネギ -> [366]キノガッサ -> [367]サンダー -> [368]タマゲタケ -> [369]ケケンカニ -> [370]ニャビー -> [371]ピッピ -> [372]ヒノヤコマ -> [373]マフォクシー -> [374]ジャローダ -> [375]タマタマ -> [376]マッスグマ -> [377]マスキッパ -> [378]パルキア -> [379]アマルルガ -> [380]カイリキー -> [381]キルリア -> [382]アーボ -> [383]ホーホー -> [384]ホイーガ -> [385]カブト -> [386]トランセル -> [387]ルナトーン

最後が「ン」で終わり、まとまりがよろしいようで。

終わりに

いかがでしたでしょうか。みなさんの疑問も解決できたのではないのでしょうか。 組合せ最適化問題なので、数が増えると時間が爆発的に増えていきます。ポケモンがこの先増えていくと、より早いアルゴリズムが必要になって来るかもしれません。

また、2017年11月17日にポケモン新作が発売されるらしいです。新キャラ出るのかな?新キャラ出たらまた最新版ポケモンを更新しなければいけないですね。乞うご期待!

しかし、802匹も覚えられるんだろうか...

【だまされないために】研究者が教える、グラフで人をだます印象操作法

こんにちは、ほけきよです。

先日こんな本を読みました。

統計でウソをつく法 (ブルーバックス)

統計でウソをつく法 (ブルーバックス)

50年前から名著と呼ばれている本です。正直そこまで新しい知識が得られるなんてことはありませんでした。 やっぱり私は数式が出てこないとピンとこない人間のようです。

しかし、統計を知らない人、数式アレルギーの人にはおすすめです。

  • データのサンプルは偏ってないか注意しようね
  • いつも使われている言葉(平均とか)がどういう意味か見つめ直そうね
  • グラフィカルな印象操作には注意しようね

とかですね。それを数式を使わず丁寧に説明してくれるので、一度読んで見てもいいのでは、と思います。

今日は、その中でグラフィカルな印象操作について掘り下げたいと思います。 この本を読んだ正直な感想は"うーん、物足りない!!“でした。なので、普段データに慣れ親しんでいる研究者が、あなたたちの印象を全力で操作します

激増しているように騙す

まずは、この二つのグラフを見比べてみましょうか。

さて、どちらが急成長しているように見えますか? 圧倒的に赤色のほうが急成長しているようです。 しかし、赤と青のグラフは同じ数値のグラフです。 違うのはy軸の範囲です。

このように、y軸の始まる位置をちょこっと変えるだけで、あたかも圧倒的成長をしているように見えるのです。

同じことが棒グラフにも出来ます。むしろ棒グラフのほうが顕著に差が見えるかもしれません。

だまされないようにするためには、y軸に注意しましょう。

伸び悩んでいるように騙す。

グラフ

まずはこのグラフをご覧ください このグラフから伸び悩みはじめているような印象を受けると思います。

けれど、実際の所どうなんでしょう。

本当は?

これも同じでy軸に注目してみてください。[:tex:10^{0}, 10^{1}]などとなっていると思います。 これは、y軸が数字の桁数を見ているということです。専門用語で言うと指数オーダーで見ているものです。実際の数値でみたものが下図右です。

大きな数を扱うときに対数スケールにするとうまく特徴が出てくることも研究の世界ではよくあります*1 。これを逆手に取ることもできるよってことをちょっといいたかったのです。グラフの縦軸には注意しましょうね。

ランダムだと騙す

グラフ

まずはこの散布図をご覧ください

訳のわからないランダム模様に見えますね。実際のところ、どうなんでしょう?

本当は?

読み解いていきましょう。とりあえず、各点のつながりが全くわからないので、繋いでみます。 点の前後の繋がりを見るにはつなぐのが一番手っ取り早いですからね。すると下図左のような形が現れました。拡大したものが右図です。

結果、このランダムに見えたグラフはこのような綺麗なサインカーブだったことがわかります。

特に時系列データなどは、ランダムかどうか判断する前に、なんかしらの関係がないか、いろいろと試してみましょう*2

とりあえず線を繋いで見るのは手っ取り早いので、オススメです。

性能が悪いものをいいように騙す(Part 1)

グラフ

まずはこちらの2つのグラフの比較をご覧ください。 縦軸が何かの性能を示していて、低いほど良いと言うことにします。

  • 横軸 : タスクの数
  • 縦軸 : 全タスクを終えるのにかかる時間

とかをイメージしてもらえるといいかもしれません。

どっちが性能がいいように思いますか?graph2のほうが良さげ。と思う人も多いと思います。

本当は?

読み解いていきましょう。

とりあえず、さっき学んだ対数スケールに縦軸を変換してみます。

ここからわかるのは、上のグラフは対数スケールでは頭打ちになりそうなのに対し、下は一定で伸び続けているということです。つまり、後々、graph2が逆転するのではないかということがわかるのです。

もう少し伸ばしたものが下図右です。graph2のほうが急激に増加していっているのがわかります。

性能比較のグラフなどでは、このようにちょうどいいところで実験を打ち切っているものも多くありません。常に疑いの目を持ちましょう。

指数的な伸びかどうかを判断するには対数スケールで直線になっているかどうかを見ることをおすすめします。

性能が悪いものをいいように騙す(Part 2)

最後に、難問を。

グラフ

まずはこちらの2つのグラフの比較をご覧ください。グラフの見方はPart1と同じです。

どう見たってGraph1のほうがヤバそうですよね。実際、どうなんでしょう。

本当は?

読み解いていきましょう。 指数的な伸びかどうかを判断するポイント、もう一つあります。それは、差分を見るということです。*3 指数的なグラフは、差分も指数的な伸びなのです!というわけで、このグラフの差分を取ってみましょう。一つ前の点との差をとったものがコチラ。

左はわかりにくいですが、y軸の範囲を絞ってみてみると。どうやらgraph1は直線で、graph2は指数的な伸びであることがわかります。 つまり、いずれgraph2がgraph1を抜く可能性があるということです。実際に、もう少しx軸を伸ばして見てみましょう。

やっぱりgraph2のほうがgraph1を抜きましたね。

指数を見破る方法として

  • 対数スケールにしてみる
  • 差分をとる

がポイントです!また、グラフを見るときは途中で打ち切られている可能性に注意しましょう。

最後に

いかがでしたか?グラフの見方を変えるだけで、かなり印象が変わっていることに気づいたはずです。 広告や質があんまりよくない論文にはこういうテクニック(?)が使われたりするので、みなさんはこのような印象操作に騙されることのなき様、正しいグラフリテラシーを持ちましょう。

最後に、私が使っている印象操作法を一つ。さっきまでのグラフ、かたっ苦しいですよね。なので、私のブログでは少しでも親しみやすいように↓の様な漫画風のグラフを使うことが多いです。

作り方はこちら

このくらいなら許されると思います。笑
みなさんも、是非是非使ってみてください。ではではっ

*1:対数をうまく使った記事がコチラwww.procrasist.com

*2:他にも、自己相関係数を出してみたり、ローレンツプロットをしてみるなどいろいろと出来ます。

*3:専門的に言うと、微分値を見ると言うことです。

【数学】旧帝戦数学部門が話題だったので、解いてみた

こんにちは、ほけきよです!

なにやら怪しい大会が京都大学で繰り広げられていたようです。

こういうの、面白いですよね。というわけで、解いてみました。

※手書きです。スミマセン。。。笑

f:id:imslotter:20170702032724p:plain
画像は先程のツイートより引用

問題

問題はコチラ。

1. 積分

f:id:imslotter:20170702032936p:plain

2. 暗号

f:id:imslotter:20170622210212p:plain

3. 幾何

f:id:imslotter:20170702033011p:plain

回答

1. 積分

記号の意味を理解することがカギです!

回答はコチラ

f:id:imslotter:20170702033040j:plain

ちなみに最後の図は面積を可視化したものです。整数部分は階段状に現れるので、それを引けば良い!ってことですね。

2. 暗号

少ない情報量からいかに有用そうなものを推論出来るかがカギです!

回答はコチラ

f:id:imslotter:20170702033051j:plain

ちなみにここに載っている人物は

で、全員超有名なので、綴りをみたらわかるようにしておいて損はないですよ!

3. 幾何

きれいな回答ではないかも。かっちょいい解き方がわからなかったので若干力技

f:id:imslotter:20170702033057j:plain

ちなみにこういう問題は、図をできるだけ丁寧に書くのが意外とポイントだったりします。 いろいろと隠れた情報を引き出しやすいですからね!

おわりに

いかがでしたでしょうか? ちなみに、中身は大量のRedBullだったそうです。私も一本ほしいなぁ! 優勝は京都大学が21分だったとか。はやい!

割と素直な問題が多かった気がします。 もうちょっと難易度上げて、京大生が3時間くらい悩む問題を次は期待したいですね!ではではっ

ちなみに、第二問が一番苦戦しました。パズルっぽいのは、ちょっとニガテ。でも楽しかったです!

爆速で資料を作る!レベル別パワポショートカット集

f:id:imslotter:20170702035737p:plain

こんにちは!ほけきよです。

世の中のクリエイティブな仕事という人たちがどんな仕事をしているか知っていますか。 *1

f:id:imslotter:20170619235713p:plain

この資料作成が2倍早くなると、どうなりますか?こうなります。

f:id:imslotter:20170619235731p:plain

クリエイティブな時間を少しでも増やすために、パワポの効率化は不可欠なのです。*2

今日は、大学院から社会人にかけての数年間でパワポ職人となりつつある私が、爆速で資料を作る方法を紹介します。 絶対覚えておくべきものから、他の人と一歩差をつけられるものまで、レベル別に取り揃えるので、是非是非ご覧ください

※ 今回のはWindows PowerPoint 2016版です。互換性は各自試しながら確認してください。

初級編

超基本

パワポって何?って方も、有無も言わず30分使って覚えておきましょう。絶対に!

用途 ショートカット
コピー/切り取り/貼り付け Ctrl+C/ Ctrl+X/ Ctrl+V
太字/斜体/下線部 Ctrl+B/ Ctrl+I/ Ctrl+U
上書き保存/名前をつけて保存 Ctrl+S/ Ctrl+Shift+S
開く/閉じる Ctrl+O/ Ctrl+W
新しいスライドを追加 Ctrl+M
新しいパワポファイルを作成 Ctrl+N
グループ化/グループ解除 Ctrl+G/ Ctrl+Shift+G
戻る/ 進む Ctrl+Z/ Ctrl+Y
全選択 Ctrl+A
図形の移動 矢印
図形のサイズ Shift + 矢印
図形の回転(15°ずつ) Alt + 矢印

スライドショー編

スライドショーボタン探すのも面倒だと思うので、覚えておくと発表時に便利ですよ。

用途 ショートカット
スライドショー(始めのページから) F5
スライドショー(開いているページから) Shift + F5
該当ページに飛ぶ(スライドショー時) 数字 + Enter
スライドショーの終了 Esc

その他Tips

  • まとまったところでグループ化することで編集効率UP!
  • 線は図形の端と端をつないでおくと便利

中級編

文字列の編集

中央揃えとか、文字を大きくとかいちいちボタン押すの面倒ですよね。 これらを覚えておくと、マウスを使う手間も省け、一気に効率UP!

用途 ショートカット
文字の大きさ調整 大/小 Ctrl+Shift+> / Ctrl+Shift+<
文字列の揃え方 右/ 中央/ 左 揃え Ctrl+R/ Ctrl+E/ Ctrl+L
フォントダイアログの表示 Ctrl+T

微調整のCtrlキー

初級編のサイズ/角度調整の時に、「微調整したい!」って時があると思います。 そういう時に便利なのが、Ctrlキーです。これで、限界の微調整が可能になります!

用途 ショートカット
位置の微調整(1 pixel) Ctrl+矢印
大きさの微調整(1 pixel) Ctrl+Shift+矢印
回転の微調整(1°ずつ) Ctrl+Alt+矢印

マウス移動時Tips

図形サイズ調整等でマウスを使うときはあると思います。そういう時のTipsがこち

用途 ショートカット
角度を固定しながらサイズ調整 Shift + 線のサイズ変更
アスペクト比を固定しながらサイズ調整 Shift + 図形のサイズ変更
図の複製 Ctrlを押したまま図をマウスで移動

上級編

上級編になると、コンボ技を使っていきます。 私がよく使うコンボを紹介します。

図形を爆速で並べるための"位置揃え, 整列"

  • Alt, H, G, Aの順に押すと、位置揃えのところに行く。その後
    • 上下中央 M
    • 左右中央 C
    • 左右に整列 H
    • 上下に整列 V を押すことで、整列ができます。例えば、綺麗に縦に並べたいときは、図のような処理の仕方をします。

f:id:imslotter:20170620022848p:plain

よく使うのは以下の二つです。覚えておきましょう。

  • 横に揃える Alt H G A M Alt H G A H
  • 縦に揃える Alt H G A C Alt H G A V

数式が書きたい

数式も、Altからのコンボ技でかけます。

  • Alt, N, E, I の順で押す

こうすることで、数式エディタを開くことができます。 また、実はAlt+;の同時押しでもOKなようです。 労力はあまり変わらないので、お好きな方をどうぞ!

マウスを使わずフォント変更

中級編でやった"フォントダイアログを開く"からコンボを繋げます。 具体的には以下の順番です

  • 範囲の指定
  • Ctrl+T
  • tab一回
  • meiryo と打つ
  • Enter

こんな感じでOKです

よく使うコンボを覚える。

Altからのコンボをいくつか覚えておくと、本当に爆速になります。Altを一回押すとこんな表示になるので、自分のよく使う機能がある方はぜひコンボ化しておきましょう

f:id:imslotter:20170620001746p:plain

終わりに

いかがでしたか?ちなみに、冒頭でも話した通り、Windowsのパワーポイントです。私は普段MacLinuxユーザーですが、パワポ作りだけはWindowsを使って行います。ショートカットを覚えてしまっては、他のOSなどでいちいちマウスを動かしてられなくなるんです。

余談ですが、某青い外資コンサルティングファームで勤めていた友人は、裏表にびっしり書かれたパワポとエクセルのショートカット集を数枚眺めながら「これを覚えて使いこなせるようにしないと」と言っていました。あそこの企業は独自のショートカットも結構あるようでした。

はじめにちょっとコストをかけてでも、ショートカットは覚えることをオススメします!

皆さんも資料作りより、クリエイティブな仕事をしたいでしょ?ではではっ!

*1:ソースは私です。

*2:資料作成が減ったらなぜ事務作業が増えるのか。これは哲学的な問いであり永遠の謎です。

PROCRASIST