PostCSS

PostCSSは、高速にCSSをごにょごにょできるツール。プラグインが沢山あって色々なことができる。

postcss/postcss
PostCSS とは何か by Yoshihide Jimbo
PostCSS まとめ
Webpack2でpostcssを使う
postcss/postcss-loader

プラグインは下記を使ってみることにした。
autoprefixer
cssnano
precss
react-css-modules

webpack2で使ってみる

$ npm install –save-dev postcss postcss-loader autoprefixer cssnano precss react-css-modules

postcss.coonfig.js

module.exports = {
  plugins: [
    require('autoprefixer')({}),
    require('cssnano')({}),
    require('precss')({}),
    require('react-css-modules')({}),
  ]
}

webpack.config.js

var path = require('path');
const webpack = require('webpack');
var ExtractTextPlugin = require('extract-text-webpack-plugin');

module.exports = {
  entry: './app/index.js',
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist')
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: ExtractTextPlugin.extract({
          fallback: "style-loader",
          use: [
            'css-loader',
            'postcss-loader'
          ],
        })
      },
      {
        test: /\.js$/,
        exclude: /(node_modules)/,
        use: [{
          loader: 'babel-loader',
          options: {
            presets: [['es2015', {modules: false}]],
            plugins: ['syntax-dynamic-import']
          }
        }]
      }
    ]
  },
  plugins: [
    new ExtractTextPlugin('styles.css'),
    new webpack.optimize.UglifyJsPlugin({
      beautify: false,
      mangle: {
        screw_ie8: true,
        keep_fnames: true
      },
      compress: {
        screw_ie8: true
      },
      comments: false
    })
  ]
};

hoge.css

$blue: #09c;
body{
    background:black;
    color:white;
}
h1{
    color:$blue;
    display: flex;
}
.hoge{
    color:red;
}
$ webpack

style.css

body{background:#000;color:#fff}h1{color:#09c;display:-webkit-box;display:-ms-flexbox;display:flex}.hoge{color:red}

CakePHP2 – Pingサーバを構築してブログの更新通知を受け取る

XML-RPCという仕組みが使われていて、大体weblogUpdates.extendedPingか、weblogUpdates.pingというメソッド名が使われる。IXR_Library.phpというライブラリがあるのでこれを使って作る。飛んでくる情報の最小単位はブログ名とブログURLなので、ブログURLからRSSのURLを取得して、RSSから最新記事のURLとタイトルを取得する。WordPressがweblogUpdates.extendedPingで、アメブロとライブドアが、weblogUpdates.pingだった。wordpressは、array(ブログ名、ブログURL、RSS_URL)できた。RSS URLを取得するのにphpQuery使おうと思ったけど、どうもライブドアだとエラーになるから使うのやめた。あとアメブロのRSSは、新規投稿が1分後くらいにしか反映されないから、CRONで5分後に再チェックするようにした。

<?php
App::uses('AppController', 'Controller');
App::import('Vendor', 'xmlrpc');
//App::import('Vendor', 'phpquery/phpQuery/phpQuery');

class PingController extends AppController
{
    public $uses = array('Site');

    public function beforeFilter()
    {
        parent::beforeFilter();
        $this->Components->disable('Security');
        $this->Auth->allow(array('index'));
        Configure::write('debug', 0);
        $this->autoRender = false;
    }

    public function index()
    {
        $callbacks = array();
        $callbacks['weblogUpdates.ping'] = array(&$this, '_updates');
        $callbacks['weblogUpdates.extendedPing'] = array(&$this, '_updates_extended');
        $callbacks['weblogUpdates.pbping'] = array(&$this, '_pbping');
        $this->server = new IXR_Server($callbacks);
    }

    public function _updates($data)
    {
        $this->log('weblogUpdates.ping', 'debug');
        if(empty($data[1])){
            $this->log('Fail to updates.', 'debug');
            $this->log($data, 'debug');
            return false;
        }
        if($this->_get_new_post($data[1])){
            return true;
        }
        return false;
    }

    public function _updates_extended($data)
    {
        $this->log('weblogUpdates.extendedPing', 'debug');
        if(empty($data[1]) || empty($data[2])){
            $this->log('Fail to updates_extended.', 'debug');
            $this->log($data, 'debug');
            return false;
        }
        if($this->_get_new_post($data[1], $data[2])){
            return true;
        }
        return false;
    }

    public function _pbping($data)
    {
        $this->log('weblogUpdates.pbping', 'debug');
        $this->log($data);
    }

    private function _get_new_post($blog_url, $rss_url = null)
    {
        if(!$site = $this->Site->findByUrlForPing($blog_url)) return false;
        if(!$rss_url){
            if(!$rss_url = $this->_get_rss_url($blog_url)) return false;
        }
        if(!$rss = $this->_get_rss($rss_url)) return false;
        if(!$this->Site->saveNewPost($rss['url'], $rss['title'], $site['Site']['id'])){
            $this->log('Fail to save new post', 'debug');
            return false;
        }
        return true;
    }

    private function _get_rss_url($blog_url)
    {
        $pattern = '/type="application\/rss\+xml".+?href="(.+?)"/s';
        $html = file_get_contents($blog_url);
        if(!preg_match($pattern, $html, $match)){
            $this->log('Not Match. '.$blog_url, 'debug');
            return null;
        }
        return $match[1];
    }

    //private function _get_rss_url($blog_url)
    //{
    //    try{
    //        $doc = phpQuery::newDocumentFile($blog_url);
    //        return pq($doc)->find('link[type=application/rss+xml]:first')->attr('href');
    //    }catch(Exception $e){
    //        $this->log('Fail to get rss url: '.$e->getCode().'|'.$e->getMessage(), 'debug');
    //        return null;
    //    }
    //}

    private function _get_rss($rss_url)
    {
        try{
            $rss = Xml::build($rss_url);
            if(!empty($rss->channel->item[0])){
                $item = $rss->channel->item[0];
            }elseif(!empty($rss->item[0])){
                $item = $rss->item[0];
            }
            return array(
                'url' => (string)$item->link,
                'title' => (string)$item->title
            );
        }catch(Exception $e){
            $this->log('Fail to get rss: '.$e->getCode().'|'.$e->getMessage(), 'debug');
            return null;
        }
    }

}

参考:
更新Pingの送信
Pingを送信する!
Google ブログ検索の更新通知 API について
CakePHP + 更新Ping送信で更新情報サービスを利用する方法
IXRライブラリの使い方
utils/Model/Behavior/PingbackableBehavior.php
How-to-create-an-XML-RPC-server-with-CakePHP.rst
CakePHP-Wordpress-Datasource/Vendor/IXR_Library.php
[42-3] link rel=”alternate” で他言語サイトやモバイル版へ導こう

CakePHP2 – RSS取得

https://book.cakephp.org/2.0/ja/core-utility-libraries/xml.html

App::uses('Xml', 'Utility');
public function rss()
{
    $url = 'http://endoyuta.com/feed/';
    $info = $this->_get_rss($url);
    if(!$info) return false;
    $this->log($info);
}

private function _get_rss($url)
{
    try{
        $rss = Xml::build($url);
        $item = $rss->channel->item[0];
        return array(
            'site_url' => $rss->channel->link,
            'item_url' => $item->link,
            'item_title' => $item->title
        );
    }catch(Exception $e){
        $this->log('Fail to get rss: '.$e->getCode().'|'.$e->getMessage(), 'debug');
        return null;
    }
}

CakePHP2 – phpQueryを使う

phpQuery
Github:TobiaszCudnik/phpquery
Composer対応したやつ:zomberg/phpquery

使い方

<?php
App::uses('AppController', 'Controller');
App::import('Vendor', 'phpquery/phpQuery/phpQuery');

class HogeController extends AppController
{
    public function index()
    {
        $doc = phpQuery::newDocumentFile("http://endoyuta.com");
        echo pq($doc)->find('link[type=application/rss+xml]:first')->attr('href');
    }
}

CakePHP3 – Log出力にMonologを使う

Seldaek/monolog

$ composer require monolog/monolog

bootstrap.phpに下記を追加

include 'logger.php';

config/logger.phpに下記を追加

<?php
use Cake\Log\Log;
use Monolog\Logger;
use Monolog\Handler\RotatingFileHandler;
use Monolog\Formatter\LineFormatter;

Log::drop('error');
Log::config('error', function () {
    $log = new Logger('app');
    $formatter = new LineFormatter(null, null, true);
    $handler = new RotatingFileHandler(LOGS.'error.log', 10);
    $handler->setFormatter($formatter);
    $log->pushHandler($handler);
    return $log;
});

こんな感じで使える。Slackとかに飛ばしたりしたいというときに役立ちそう。

cakeの標準機能でログ出力の調整をする場合は、app.phpのLog設定箇所を変更する。

'Log' => [
    'debug' => [
        'className' => 'Cake\Log\Engine\FileLog',
        'path' => LOGS,
        'file' => 'debug-'.date('Y-m-d'),
        'levels' => ['notice', 'info', 'debug'],
        'url' => env('LOG_DEBUG_URL', null),
        'rotate' => 10,
        'size' => '1MB'
    ],
    'error' => [
        'className' => 'Cake\Log\Engine\FileLog',
        'path' => LOGS,
        'file' => 'error-'.date('Y-m-d'),
        'levels' => ['warning', 'error', 'critical', 'alert', 'emergency'],
        'url' => env('LOG_ERROR_URL', null),
        'rotate' => 20,
        'size' => '1MB'
    ],
],

CakePHPで作られたquickapps/cmsを使ってみる

quickapps/cmsは、CakePHPで作成されたCMSだそうです。ライセンスはGPL-3.0dだそうです。GitHubスターが150個くらい。試しにインストールしてみます。

インストール

$ composer create-project -s dev quickapps/website [website_name]

インストール画面


結構かっこいい。ワードプレスのようなインストール画面でインストール終わったらちゃんと表示された。

ホームページ


フォルダ構成が不思議な感じ。全部プラグイン内で動いてるみたいで、srcフォルダもない。

CakePHP3 – MinifyHtmlプラグイン

WyriHaximus/MinifyHtml

HTMLを圧縮して出力してくれるプラグイン。

インストール

$ composer require wyrihaximus/minify-html

bootstrap.phpに下記を追加。

Plugin::load('WyriHaximus/MinifyHtml', ['bootstrap' => true]);

設定

AppView.phpに下記を追加。

public function initialize()
{
    $this->loadHelper('WyriHaximus/MinifyHtml.MinifyHtml');
}

デバッグモードだと圧縮しない。本番モードなら上記だけで圧縮してくれる。

CakePHP3 – whoopsプラグイン

gourmet/whoops

whoopsという、かっこいいエラー画面にしてくれるプラグイン。

$ composer require gourmet/whoops:~1.0

bootstrap.phpの下記ErrorHandlerの箇所を修正する。

(new ErrorHandler(Configure::read('Error')))->register();

修正後

(new \Gourmet\Whoops\Error\WhoopsHandler(Configure::read('Error')))->register();

404の場合は変わらないけど、500の場合は、下記のエラー画面に変わった。確かにきれいでわかり易い。

CakePHP3用のよさそうなプラグイン

FriendsOfCake/awesome-cakephp

認証系

FriendsOfCake/Authenticate
CakeDC/users
dereuromark/cakephp-tinyauth
ivanamat/cakephp3-captcha
ADmad/cakephp-jwt-auth
ADmad/CakePHP-HybridAuth

認可系

ivanamat/cakephp3-aclmanager
JcPires/CakePhp3-AclManager
PilOop/cakephp3-auth-acl-bootstrap-adminlte

アップロード系

josegonzalez/cakephp-upload
jrbasso/MeioUpload
davidyell/CakePHP3-Proffer

管理画面系

FriendsOfCake/crud-view
prakashw3expert/cakephp3.0-admin
KarlJakober/CakeAutoAdmin

Bootstrap系

FriendsOfCake/bootstrap-ui

REST API系

CakeDC/cakephp-api
multidots/cakephp-rest-api
hantsy/angularjs-cakephp-sample
Wizehive/cakephp-api-utils
patarkf/cakephp3restapi

グラフ描画系

netusco/CakePhp3-ChartJs
scottharwell/GoogleCharts

ソーシャル系

webtechnick/CakePHP-Facebook-Plugin

画像系

burzum/cakephp-imagine-plugin

検索系

CakeDC/search
FriendsOfCake/search

色々入ってる系

dereuromark/cakephp-tools

CRUD系

FriendsOfCake/crud

カテゴリー・タグ・お気に入り・コメント・評価系

CakeDC/tags
CakeDC/categories
CakeDC/favorites
CakeDC/comments
CakeDC/ratings

CSV系

FriendsOfCake/CakePdf
FriendsOfCake/cakephp-csvview

PDF系

FriendsOfCake/CakePdf

Asset系

markstory/asset_compress

ストレージ系

burzum/cakephp-file-storage

キャッシュ系

ceeram/clear_cache

CMS系

croogo/croogo

チケット管理ツール系

yandod/candycane

Q&Aサイト系

Datawalke/Coordino

WYSIWYG系

CakeDC/TinyMCE

Markdown系

ivanamat/cakephp3-markdown

開発サポート系

josegonzalez/cakephp-mail-preview
ebrigham1/cakephp-error-email

その他

passbolt/passbolt_api
josegonzalez/cakephp-environments
josegonzalez/cakephp-queuesadilla
fheider/cakephp-datatables
scherersoftware/cake-notifications

cakephp3 – DB設定時のTime Zoneエラー

Windows10でXampp使ってcakephp3を動かすとき、DB設定のtimezoneをAsia/Tokyoにすると下記エラーがでた。
General error: 1298 Unknown or incorrect time zone: ‘Asia/Tokyo’

対処法は、ここのとおりやったらできた。

ここから、timezone_2017a_posix_sql.zipをダウンロード・展開して、出てきたsqlファイルをどこかに置く。

そして、下記のようにやる。
$ cd /d/xampp/mysql/bin
$ ./mysql -u root mysql -p < /e/timezone_posix.sql

GCPでUbuntu16・Nginx・PHP7・cakePHP3環境をつくる

Google Cloud Platform(GCP)はAWSのgoogle版です。ここにサービスの説明があります。

Compute Engine

まずプロジェクトを作成する。次に、Compute Engineのコンソールで、VMインスタンスを作成する。ゾーンを日本、マシンタイプをf1-micro、OSをUbuntu16.10で作成してみた。数十秒程度でインスタンスが作成完了し、ブラウザ上でssh接続、ターミナル操作がすぐ可能になった。

マシンタイプの変更方法

参考:vm instanceのMachine type変更方法
IPをスタティックにして、ディスクを削除しないようにしながら、VMインスタンスを削除して、新しくインスタンスをつくるらしい。この間サーバは止まってしまう。でもいずれにしても結構簡単に切替は可能。

ローカル環境でssh接続できるようにする

google cloud SDKをインストールする。インストール完了したら初期設定が動き出すので、ログインしたりゾーン選択したりする。
Compute EngineのVMインスタンスの画面にある、ssh接続用のgcloudコマンドを実行する。これでssh接続できた。

PHPとかNginxとかをインストールする

$ sudo apt-get update
$ sudo apt-get install php
$ php -v
PHP 7.0.15-0ubuntu0.16.10.4 (cli) ( NTS )
$ sudo apt-get install nginx
$ nginx -v
nginx version: nginx/1.10.1 (Ubuntu)
$ sudo apt-get install git vim composer

デフォルトでapache2が入っていて起動されているようだ。
$ sudo systemctl stop apache2
$ sudo systemctl start nginx

Cloud DNS

https://cloud.google.com/dns/quickstart
上記に従って、DNS APIを有効にして、DNSコンソールからドメインを追加する。記載されているネームサーバに合わせて、ドメインのネームサーバを変更して、必要な設定をすると反映される。

Cloud SQL

Google Cloud SQL ドキュメント
Cloud DNSと同様にAPIを有効にしてインスタンスを作成する。Compute Engineから接続するには、ネットワークの承認が必要。下記にやり方が書いてあった。
MySQL クライアントを Compute Engine から接続する

Compute Engineを設定してCakephp3を動かす

$ sudo vim /etc/nginx/sites-available/default

server {
    listen 80;
    server_name hoge.com;
    root /var/www/html/hoge.com/webroot;
    index index.php;
    location / {
        try_files $uri $uri?$args $uri/ /index.php?$uri&$args /index.php?$args;
    }
    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/run/php/php7.0-fpm.sock;
    }
}

$ sudo systemctl restart nginx
$ cd /var/www/html
$ sudo apt-get install php-fpm php-intl php-mbstring php-zip
$ sudo composer create-project –prefer-dist cakephp/app hoge.com

Compute EngineをCloud SQLにつなぐ

phpmyadminを使う

$ sudo apt-get install mysql-client
$ sudo apt-get install phpmyadmin
$ sudo vim /etc/phpmyadmin/config-db.php

config-db.php

$dbname='hoge';
$dbserver='<Cloud SQLのIPアドレス>';
$dbport='3306';
$dbtype='mysql';

$dbnameがないとエラーになるけど、内容は適当でも大丈夫だった。

$ sudo echo “hoge:$(openssl passwd -apr1 hogepassword)” > /path/to/hogepath
$ sudo chmod 604 /path/to/hogepath
$ sudo vim /etc/nginx/sites-available/default

#Phpmyadmin
server {
    listen 80;
    server_name hogehoge.com;
    auth_basic 'input';
    auth_basic_user_file /path/to/hogepath;
    root /usr/share/phpmyadmin;
    location / {
        index index.php;
    }
    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/run/php/php7.0-fpm.sock;
    }
}

コマンドラインでmysqlにつなぐ

$ mysql –host=[INSTANCE_IP_ADDR] –user=root -p

cakephp3でmysqlにつなぐ

$ sudo vim /var/www/html/hoge.com/config/app.php
hostをCloud SQLにする。

Let’s EncryptでSSL取得・設定

hoge.comとphpmyadminをhttps接続のみ可能にする。hoge.comにhttpアクセスした場合はhttpsにリダイレクトさせる。

$ sudo apt-get install letsencrypt
$ sudo systemctl stop nginx
$ sudo letsencrypt certonly –standalone -d hoge.com
$ sudo letsencrypt certonly –standalone -d hogehoge.com
$ sudo systemctl start nginx
$ sudo vim /etc/nginx/sites-available/default

#hoge.com cakephp3
server {
    listen 80;
    server_name hoge.com;
    return 301 https://$host$request_uri;
}
server {
    listen 443;
    ssl on;
    ssl_certificate /etc/letsencrypt/live/hoge.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/hoge.com/privkey.pem;
    server_name hoge.com;
    root /var/www/html/hoge.com/webroot;
    index index.php;
    location / {
        try_files $uri $uri?$args $uri/ /index.php?$uri&$args /index.php?$args;
    }
    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/run/php/php7.0-fpm.sock;
    }
}
#Phpmyadmin
server {
    listen 443;
    ssl on;
    ssl_certificate /etc/letsencrypt/live/hogehoge.com/cert.pem;
    ssl_certificate_key /etc/letsencrypt/live/hogehoge.com/privkey.pem;
    server_name hogehoge.com;
    auth_basic 'input';
    auth_basic_user_file /path/to/hogepath;
    root /usr/share/phpmyadmin;
    location / {
        index index.php;
    }
    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/run/php/php7.0-fpm.sock;
    }
}

$ sudo systemctl restart nginx

Python3 – 対数

Pythonで対数を出すには、math.logを使います。import mathで使えるようになります。

import math
print(math.log(2, 10))

0.30102999566398114

これは、10を何乗したら2になるか?です。log102です。
試しに、10を0.30102999566398114乗してみます。

print(pow(10, 0.30102999566398114))

1.9999999999999998

100の0.5乗は、100の平方根なので10です。

print(pow(100, 0.5))

10.0

27の0.3333333333333333333333333333333333333333333乗は、大体27の立方根なので、3に近いはずです。

print(pow(27, 0.3333333333333333333333333333333333333333333))

3.0

おージャスト3.0になった。

print(pow(27, 0.333333333333333))

2.9999999999999964

print(pow(27, 0.3333333333333333))

3.0

少数15桁までだと2.99…になりますが、16桁にするとジャスト3.0になる。

10を底とする対数を常用対数といいます。

対数関数と指数関数は逆関数の関係にあります。逆関数はy=xに対して対称です。指数関数と対数関数のグラフを書いてみます。
y = axの指数関数と、y = logaxの対数関数を書きます。

numpyを使いたいので、math.logではなくnp.logを使います。aを2として、np.log2を使います。例えばnp.log2(4)とすると2になります。2を底とした4の対数を出しています。

import numpy as np
import matplotlib.pyplot as plt

x = np.arange(0.1, 3, 0.1)
y = x
plt.plot(x, y, label='y=x')
y = 2 ** x
plt.plot(x, y, label='y=2**x')
y = np.log2(x)
plt.plot(x, y, label='y=log2 x')
plt.grid(True)
plt.legend()
plt.show()

対称なのか全然これじゃ分からない。

対数関数は、
対数が1より小さいとき、
底が1より大きければ、真数は底より小さい。
底が1より小さければ、真数は底より大きくなる。

対数関数を、底の大きさのパターンで2つ書いてみる。

import math
import numpy as np
import matplotlib.pyplot as plt

#底は1ではない正の数
#底が1より大きい場合
a = 2
#底が1より小さい場合
b = 0.5
#xは真数
x_list = np.arange(0.1, 3, 0.1).tolist()
#yは対数
y_a = []
y_b = []
for x in x_list:
    y_a.append(math.log(x, a))
    y_b.append(math.log(x, b))
plt.plot(x_list, y_a)
plt.plot(x_list, y_b)
plt.grid(True)
plt.show()

2100の桁数を出すには、常用対数を使います。
log102100 = 100log102 = 100 * 0.30102999566398114 = 30.102999566398114
よって31桁です。10の2乗は100なので3桁です。10の3乗は1000で4桁です。2.1乗は100より大きく、1000より小さいので3桁です。30.10乗は31桁です。

Python3 – xのn乗のグラフ(matplotlibのsubplotとアニメーション)

GIFアニメーション

matplotlibのアニメーション作成は2つ種類があって、ArtistAnimationとFuncAnimationとがある。

参考:matplotlib でアニメーションを作る

ArtistAnimation は、あらかじめ全てのグラフを配列の形で用意しておき、それを1枚ずつ流すというものである。FuncAnimationは予め完成したグラフを渡すのではなく、アニメーションの1フレームごとに関数を実行する。データが巨大すぎる場合や潜在的に無限に続く場合に便利。

ArtistAnimation

ArtistAnimationを使って、xの2乗~10乗のグラフを配列に入れて、アニメーションをつくってみる。

サンプルコード

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation

def xpown(s, e):
    graphs = []
    fig = plt.figure()
    for n in range(s, e + 1):
        x = np.arange(-100, 100, 0.1)
        y = x ** n
        g = plt.plot(x, y, label='y = x ** {}'.format(n))
        graphs.append(g)
    plt.legend()
    ani = animation.ArtistAnimation(fig, graphs, interval=500)
    ani.save('pow1.gif', writer='imagemagick')

if __name__ == '__main__':
    xpown(2, 10)

結果

これだと、10乗のグラフのyが大きすぎて、他がみんな直線みたいになってしまった。

FuncAnimation

FuncAnimationを使ってやってみる。

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation

x = np.arange(-100, 100, 0.1)
n = 1
max = 10
fig = plt.figure()

def plot_pow(data):
    plt.cla()
    global n
    n = n + 1 if n < max else 2
    y = x ** n
    im = plt.plot(x, y, label='y = x ** {}'.format(n))
    plt.legend()

if __name__ == '__main__':
    ani = animation.FuncAnimation(fig, plot_pow, interval=400, frames=9)
    ani.save("pow2.gif", writer="imagemagick")

結果

指数が偶数ならy軸に対称のグラフになり、奇数なら原点に対象のグラフになります。原点に対象っていうのか。

Subplot

matplotlibで複数のグラフを表示したいときにsubplotが使えます。
参考:[Python]Matplotlibで複数のグラフを描画する方法

サンプルコード

import numpy as np
import matplotlib.pyplot as plt

x = np.arange(-100, 100, 0.1)
fig = plt.figure()
ax = []
for i in range(9):
    y = x ** (i ++ 2)
    ax.append(plt.subplot2grid((3, 3), (i // 3, i % 3)))
    ax[i].set_title('y = x ** {}'.format(i + 2))
    ax[i].plot(x, y)
plt.show()

結果

Python3 – 変数のスコープについて

下記コードのとき結果は1と表示されます。

n = 1
def hoge():
    print(n)
hoge()

下記コードのときエラーになります。

n = 1
def hoge():
    n += 1
    print(n)
hoge()

UnboundLocalError: local variable ‘n’ referenced before assignment

参考:なぜ変数に値があるのに UnboundLocalError が出るのですか?

これは、あるスコープの中で変数に代入を行うとき、その変数はそのスコープに対してローカルになり、外のスコープにある同じ名前の変数を隠すからです。foo の最後の文が x に新しい値を代入しているので、コンパイラはこれをローカル変数であると認識します。その結果、先の print(x) が初期化されていないローカル変数を表示しようとして結果はエラーとなります。

globalだよと宣言すると大丈夫になります。

n = 1
def hoge():
    global n
    n += 1
    print(n)
hoge()

ネストされたスコープだと、nonlocalが使えます。

def hoge():
    n = 1
    def page():
        nonlocal n
        n += 1
        print(n)
    page()
hoge()

Python3 – matplotlibでアニメーションGIFをつくる

matplotlib.animatioを使うとアニメーションがつくれます。自分の環境ではgifで保存しようとしたら、imagemagickがなくてエラーになりました。imagemagickのインストールとかはここに書いてありました。

Imagemagick

http://www.imagemagick.org/script/binary-releases.php#windows

Imagemagickのパスをmatplotlibに設定する必要がある。下記のようにするとmatplotlibが見ている設定ファイルの場所がわかる。

import matplotlib
print(matplotlib.matplotlib_fname())

自分はユーザディレクトリ内のAnaconda3の中の下記だった。

Anaconda3\lib\site-packages\matplotlib\mpl-data\matplotlibrc

matplotlibrcの最後の行らへんに、下記行があるので、ここにImagemagickのパスを入れる。

#animation.convert_path: 'convert' # Path to ImageMagick's convert binary.

コメントを外して、下記のように入力します。入力の際、パスを「’」で囲むと下記のようなエラーになります。

animation.convert_path: D:\ImageMagick-7.0.4-Q16\magick.exe


C:\Users\hoge\Anaconda3\lib\site-packages\matplotlib\animation.py:782: UserWarning: MovieWriter imagemagick unavailable
warnings.warn(“MovieWriter %s unavailable” % writer)
Traceback (most recent call last):
File “C:/Users/hoge/hoge.py”, line 15, in
ani.save(‘sample.gif’, writer=’imagemagick’)
File “C:\Users\hoge\Anaconda3\lib\site-packages\matplotlib\animation.py”, line 810, in save
writer.grab_frame(**savefig_kwargs)
File “C:\Users\hoge\Anaconda3\lib\contextlib.py”, line 66, in __exit__
next(self.gen)
File “C:\Users\hoge\Anaconda3\lib\site-packages\matplotlib\animation.py”, line 196, in saving
self.finish()
File “C:\Users\hoge\Anaconda3\lib\site-packages\matplotlib\animation.py”, line 389, in finish
+ ‘ Try running with –verbose-debug’)
RuntimeError: Error creating movie, return code: 1 Try running with –verbose-debug

サンプルコード

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation

def anim():
    graphs = []
    fig = plt.figure()
    x = np.arange(-50, 50, 0.1)
    y = x ** 2
    for i in range(50):
        plt.plot(x, y)
        g = plt.plot(i, i ** 2, 'o')
        graphs.append(g)
    ani = animation.ArtistAnimation(fig, graphs, interval=50)
    plt.show()
    ani.save('anim.gif', writer='imagemagick')

if __name__ == '__main__':
    anim()