いっしきまさひこBLOG

AI・機械学習関連、Web制作関連、プログラミング関連、旅行記録などなど。一色政彦。

matplotlibのグラフで日本語を使う手法まとめ

問題と、この記事の目的

matplotlibのグラフを描画するときにタイトルや凡例などに日本語を使うと、次のように通常は文字化けしてしまいます(豆腐□になる)。

f:id:misshiki:20180815145120p:plain

これを回避して次のように日本語で表示する方法を、自分用の備忘録として、また他の人の参考用に、まとめておきます。

f:id:misshiki:20180815145249p:plain

フォントを指定する方法には、主に次の3段階があります。

  • 1.【個別のフォント】を設定
  • 2. 【以下すべてのmatplotlibのフォント】をまとめて設定
  • 3.【matplotlib全体のデフォルトフォント】を設定

それぞれ説明していきます。

フォントのダウンロード

その前に必要に応じて使うフォントを、OSにインストールしておいてください。この記事では以下を使います。

1. 【個別のフォント】を設定

1の手法の利点は、必要なところだけフォントを設定できるので、より細かくカスタマイズできるところです。実行するコードは、次のようになります。なお、axaxes(figure図の中にあるグラフ本体)のことです。

# 1. 【個別のフォント】を設定できる
font = {'family': 'IPAexGothic',
        'size': 14}
ax.set_title('日本語タイトル', fontdict=font)
ax.set_xlabel('X軸', fontdict=font)
ax.set_ylabel('Y軸', fontdict=font)

ただ、この方法だと、凡例(legend)が設定できないらしい。凡例を日本語化するには、以下のようにFontPropertiesを使う必要があります。

from matplotlib.font_manager import FontProperties
fp = FontProperties(fname=r'C:\WINDOWS\Fonts\SourceHanCodeJP-Regular.otf', size=14) # 他にはipaexg.ttfフォント
ax.set_title('日本語タイトル', fontproperties=fp)
ax.legend(['凡例だけはprop=fp', 'それ以外はfontproperties=fp'], loc='upper left', prop=fp)

FontPropertiesを使うデメリットは、フォントファイルを直接扱っているので環境依存が強まる点だと思います。

2. 【以下すべてのmatplotlibのフォント】をまとめて設定

次がmatplotlibのrc(“run commands”: いわゆる実行・挙動の構成)をダイナミックに設定する方法です。あくまでダイナミックなので、その実行において一時的に適用されます。

# 2.【以下すべてのmatplotlibのフォント】をまとめて設定できる
import matplotlib
font = {'family' : 'SourceHanCodeJP-Regular',
        'size'   : 14}
matplotlib.rc('font', **font)

以下のように設定することも可能です。

import matplotlib.pyplot as plt
plt.rcParams['font.family'] = 'IPAexGothic'
plt.rcParams['font.size'] = 14

1のようにすべてにフォントを設定しなくてよいので便利です。また、実行のたびにフォントを変えられるので、手法の中では一番オススメです。

3. 【matplotlib全体のデフォルトフォント】を設定

上記のように、個別に設定したり、実行ごとに設定設定すたりするのが面倒な場合は、matplotlib全体の構成を担うrcファイルで、デフォルトのフォントを設定してしまうとよいです。

これにはまず以下を実行して、matplotlibrcの場所を取得します。

# 3. 【matplotlib全体のデフォルトフォント】を設定できる
import matplotlib
# 設定ファイルの場所を取得
print(matplotlib.matplotlib_fname())
# 取得例: 'C:\Users\masa-i\Miniconda3\envs\introtensorflow\lib\site-packages\matplotlib\mpl-data\matplotlibrc'

次に、このファイルをエディターで開き、[font.family]の値を編集して保存してください。

f:id:misshiki:20180815155222p:plain

フォントはキャッシュされているらしいので、次のコードを実行して、いったん再構築しておきます。

# フォントキャッシュを再構築
matplotlib.font_manager._rebuild()

さらに、Jupyter Notebookを使っている場合は、いったんカーネルを再起動してください。これをしないと、正常にmatplotlibrcファイルの設定が反映しないようでした。

よく日本語を使う場合は、matplotlibrcファイルでデフォルトフォントを設定しておくと便利だと思います。

まとめ

以上、matplotlibの日本語設定方法をまとめました。

ネット上でたくさんヒットするのですが、情報が分散していて調べるのに苦労しました。今後もよく使う内容だと思うので、調べた内容をここにまとめ直しました。

Hope this helps.

カスタムURLを指定した、はてなブログAtomPubによる記事投稿

はてなブログでは、カスタムURL(custom-url、CustomPath)が指定できます。

しかしAtomPubを使う場合は指定できないという問題があります。少なくとも公式のヘルプには指定方法が記載されていません。

上記のコンソールアプリは、はてなのMackerelのドキュメント作成で使われているもので、これには --custom-path というカスタムURLを指定するためのオプションが用意されていました。

中身を調べてみると、blogsync/atom.go at master · motemen/blogsyncxml:"http://www.hatena.ne.jp/info/xmlns#hatenablog custom-url,omitempty" という記載があり、XML名前空間に「http://www.hatena.ne.jp/info/xmlns#hatenablog」を設定した <custom-url> 要素にカスタムURLを指定できることが分かります。

実際にAdvanced REST clientというツールで、HTTP POST送信のBodyに指定して検証すると、確かに動作しました。ということで誰かの役に立つかもしれないので情報共有しておきます。

<?xml version="1.0" encoding="utf-8"?>
<entry xmlns="http://www.w3.org/2005/Atom"
       xmlns:app="http://www.w3.org/2007/app"
       xmlns:opt="http://www.hatena.ne.jp/info/xmlns#hatenablog">
...
  <opt:custom-url>test/path</opt:custom-url>
</entry>

Microsoft MVP を12年連続受賞。Visual Studio and Development Technologies [2018-2019] カテゴリ

2007年から C#Visual Studio と開発技術のカテゴリでMicrosoft MVPを受賞していますが、今年も無事、更新できました。

Masahiko Isshiki (misshiki)

ただ、2018年4月からはDeep Insiderというサイトの編集長をやっていて、従来の.NET開発やWeb/IoTなど幅広く技術を追いかけるのをやめ、「仕事的にも個人的にもAI・機械学習・ディープラーニングの技術に専念して今後しばらくは生きていきたい」と決心しているので、本音では AI カテゴリで受章したかったです...。
まぁ2017年のBuild Insiderなどでの活動と見ると、この Visual Studio and Development Technologies カテゴリなのは仕方ないと思いますが。

テクノロジの変化に合わせて MVP アワードの分類を調整

ということも受章通知のメールに書かれているので、今後、より適切な分類に再振り分けされる可能性もあるかなと思っています。

今年はいつもといろいろと違ったのでダメかなと思っていました。まずは良かった。今回はブログ記事として書いてみました。

C#でUnicodeテキストをShift_JIS化するときに変換できないUnicode文字を代替文字で置き換える方法

C#で、UTF-8エンコードのテキストファイルを読み込んで、Shift_JISエンコードのテキストファイルを書き出すときに、変換できないUnicode文字が存在する可能性があります。そのUnicode文字の代替として、HTML文字実体参照(Character Entity Reference)の「&#x+16進数」表記文字列に置き換える方法を、この記事で共有します。

なお、文字実体参照という表記になっているのは、僕の都合なので、必要に応じて別の表現も可能です。例えば「U+16進数表記」のUnicodeスカラー値で表現することが考えられます。

サンプルコードをオープンソースで公開します。as-isでサポートもなしとさせていただきます。

ソースコードは次のリンク先を参照してください: isshiki/UnicodeToSJISfallback: https://github.com/isshiki/UnicodeToSJISfallback

内容の説明はソースコード内にたくさん書かれていますので、それを参考にしてください。

using System.IO;
using InsidersCMS;

namespace UnicodeToSJISfallback
{
    class Program
    {
        static void Main(string[] args)
        {
            var text = "㉑😀𩸽";

            var bytesSJIS = CmsUtility.Encoding.ShiftJISwithReplaceFallback.GetBytes(text);

            File.WriteAllBytes(@"C:\sample\test.txt", bytesSJIS);
        }
    }
}
using System;
using System.Text;

namespace InsidersCMS
{
    public static class CmsUtility
    {

        public static class Encoding
        {
            public static System.Text.Encoding ShiftJISwithReplaceFallback =
                System.Text.Encoding.GetEncoding("Shift_JIS", new EncoderUnicodeToSJisFallback(), DecoderFallback.ReplacementFallback);
        }

        private const int MAX_COUNT_CharacterEntityReference = 10; // Unicode文字実体参照の最大長( &#x0; ~ &#x10ffff; まで)

        // 入力文字をエンコードできない場合、このフォールバック(=エラー時の代替処理機構)が使用されるクラス。
        private class EncoderUnicodeToSJisFallback : EncoderFallback
        {
            // エンコーダーのフォールバック バッファーを提供するオブジェクトを生成して返す。
            public override EncoderFallbackBuffer CreateFallbackBuffer()
            {
                return new EncoderUnicodeToSJisFallbackBuffer();
            }

            // エンコードできなかった「入力文字」を置き換える「代替文字列」の最大文字数を返す。
            public override int MaxCharCount
            {
                get { return MAX_COUNT_CharacterEntityReference; } // 具体的には "&#x00a9;" のようなUnicode文字実体参照表記の長ささになる。
            }
        }

        // 入力文字をエンコードできないときに、エンコーダーに代替文字列を返せるようにするためのバッファーとして使われるクラス。
        private class EncoderUnicodeToSJisFallbackBuffer : EncoderFallbackBuffer
        {
            // 代替文字列のバッファー
            private string alternativeCharBuffer;

            // 代替文字列のバッファーにおける現在位置
            private int currentPosition;

            // 代替文字列への変換もできなかった場合のエラー文字(通常はないと思われるが念のため)
            private string giveupString = "★";

            public EncoderUnicodeToSJisFallbackBuffer()
            {
                Reset(); // 状態を初期化する
            }

            // 代替文字列のバッファーで、処理されずに残っている文字数。
            public override int Remaining
            {
                get { return alternativeCharBuffer.Length - currentPosition; }
            }

            // エンコードできないUnicode文字が見つかるとこのメソッドが呼び出されるので、
            // その入力文字を代替文字列に置き換えてバッファーに保存する。
            public override bool Fallback(char charUnknown, int index)
            {
                // charUnknown: 入力文字
                // index:       入力バッファーにおける文字のインデックス位置

                if (currentPosition < alternativeCharBuffer.Length)
                {
                    // ここに来るのは、現在位置が代替文字列バッファーの最後まで到達していない状態
                    throw new ArgumentException("原因がよく分からないけど、" +
                        "代替文字列バッファーの全ての文字が取得されていない状態で、" +
                        "さらに新しいUnicode文字がフォールバックされている。" +
                        "基本的には起こりえないエラーが発生してしまった。");
                }

                // このクラスでは、エンコードできない文字は「Unicode文字実体参照表記」の文字配列に置き換える。
                // ちなみに、Unicodeは1文字あたり2バイト(0x0000~0xFFFF)で、16進数の場合は「&#x+16進数」で表記する。例えば「©」を表すには「&#x00a9;」となる。
                alternativeCharBuffer = String.Format("&#x{0:x};", (int)charUnknown);
                if (alternativeCharBuffer.Length > MAX_COUNT_CharacterEntityReference)
                {
                    alternativeCharBuffer = giveupString;
                }

                currentPosition = 0; // 代替文字列バッファーを作り直したので、現在位置を先頭に初期化する。

                return true; // Unicode文字を処理できる場合は true。できずに無視する場合はfalse。
            }

            // エンコードできないUnicodeサロゲート文字が見つかるとこのメソッドが呼び出されるので、
            // その入力サロゲート文字を代替文字列に置き換えてバッファーに保存する。
            public override bool Fallback(char charUnknownHigh, char charUnknownLow, int index)
            {
                // charUnknownHigh: 入力ペアの上位サロゲート。最小値=U+D800、最大値=0xDBFF。
                // charUnknownLow:  入力ペアの下位サロゲート。最小値=0xDC00、最大値= 0xDFFF。
                // index:           入力バッファーにおけるサロゲートペアのインデックス位置。

                if (currentPosition < alternativeCharBuffer.Length)
                {
                    // ここに来るのは、現在位置が代替文字列バッファーの最後まで到達していない状態
                    throw new ArgumentException("原因がよく分からないけど、" +
                        "代替文字列バッファーの全ての文字が取得されていない状態で、" +
                        "さらに新しいUnicodeサロゲート文字がフォールバックされている。" +
                        "基本的には起こりえないエラーが発生してしまった。");
                }

                // サロゲートペアを単一文字に変換する( 0x010000 ~ 0x10ffff の範囲になるはず)。
                int surrogateChar = 0x10000
                  + ((int)charUnknownHigh - 0xD800) * 0x400
                  + ((int)charUnknownLow - 0xDC00);

                // このクラスでは、エンコードできない文字は「Unicode文字実体参照表記」の文字配列に置き換える。
                // ちなみに、Unicodeサロゲート文字は1文字あたり4バイト(0x010000~0x10ffff)で、16進数の場合は「&#x+16進数」で表記する。例えば「𪚲」を表すには「&#x02a6b2;」となる。
                alternativeCharBuffer = String.Format("&#x{0:x};", (int)surrogateChar);
                if (alternativeCharBuffer.Length > MAX_COUNT_CharacterEntityReference)
                {
                    alternativeCharBuffer = giveupString;
                }

                currentPosition = 0; // 代替文字列バッファーを作り直したので、現在位置を先頭に初期化する。

                return true; // サロゲートペアを処理できる場合は true。できずに無視する場合は false。
            }

            // 代替文字列バッファーにおける次の1文字を取得する。
            // ※代替文字列はこのクラスのバッファーから1文字ずつ取得されながら完成するので、ここは頻繁に呼び出される。
            public override char GetNextChar()
            {
                if (currentPosition >= alternativeCharBuffer.Length)
                {
                    // 現在位置がバッファーの長さに到達しているので、次の文字はない。
                    return (char)0; //「次の文字はない」という意味。
                }

                return alternativeCharBuffer[currentPosition++]; // 代替文字列バッファーの次の開始位置の1文字を返す。
            }

            // 代替文字列バッファーにおける前の文字位置に移動する。
            public override bool MovePrevious()
            {
                if (currentPosition <= 0)
                {
                    return false; // 現在位置が0では、前に移動することは不可能
                }

                currentPosition--; // 前の文字列位置へ移動

                return true; // 前の文字列位置へ移動した場合は true。それ以外の場合は false。
            }

            // フォールバックバッファーに関連するすべてのデータおよびステータス情報を初期化する。
            public override void Reset()
            {
                alternativeCharBuffer = String.Empty;
                currentPosition = 0;

                base.Reset();
            }
        }

    }
}

SlackFilesCleaner - Slackファイルを全削除するツール

必要になり、Slackファイルを全(1000件ずつ)削除するツールを作ってみたので、オープンソースで公開します。C#で作成しています。

as-isでサポートもなしとさせていただきます。

使い方やソースコードなど詳しくは下記リンク先を参照してください: isshiki/SlackFilesCleaner: Slackファイルを全(1000件ずつ)削除するツール。 https://github.com/isshiki/SlackFilesCleaner

Thunderbird add-on「ReFwdFormatter」新版をリリースしてオープンソース化

7~8年前に作ったThunderbird add-onの「ReFwdFormatter」のバージョン 1.57.0をリリースしました。Thunderbird 57.0までをサポートしています。

addons.mozilla.org

f:id:misshiki:20170908212233p:plain
ReFwdFormatter :: Thunderbird 向けアドオン(AMO)

ここ数年のAMO ( addons.mozilla.org ) の審査基準の厳格化に伴い、バージョンの自動更新に合格できなくなっていましたが、海外からのメール問い合わせもあり、重い腰を上げて一気にコードを書き直しました。ロジックも微妙に変わっているところもあるかも。

そしてコードも今の時代に合わせてオープンソースに変更しました。

github.com

アドオン開発ばかりに構ってもいられないので、協働で開発・公開作業をしてくる GitHub Collaborator (開発作業) & Add-on Authors - Developer for AMO (公開作業) を募集します。お気軽にGitHub経由などでご連絡ください。