Amazon Translate を試してみた

ちょっとAmazon Translateに興味が湧いて試してみたのでそのメモです。
結論ですが、非常に簡単に翻訳ができることがわかりました。

準備として、 boto3 が使えるようにしておく必要があります。(アクセスキーやシークレットキーの準備。自分は環境変数に入れています)
また、利用するIAMにAmazon Translateの権限を付与しておく必要があります。

例文はいつも通り「走れメロス」から拝借しています。

翻訳対象のテキストはこちらです。


text = """
メロスは激怒した。
必ず、かの邪智暴虐の王を除かなければならぬと決意した。
メロスには政治がわからぬ。
メロスは、村の牧人である。
笛を吹き、羊と遊んで暮して来た。
けれども邪悪に対しては、人一倍に敏感であった。
きょう未明メロスは村を出発し、野を越え山越え、十里はなれた此このシラクスの市にやって来た。
メロスには父も、母も無い。
女房も無い。
十六の、内気な妹と二人暮しだ。
"""

Amazon Translate をboto3から使う方法は非常に簡単で、client を生成して、
translate_textテキストに、ほんやくしたいテキスト、元の言語、翻訳先の言語を指定するだけです。
今回は 日本語 -> 英語 を指定しました。

ちなみにドキュメントはこちらです。


import boto3
client = boto3.client('translate')

result = client.translate_text(
    Text=text,
    SourceLanguageCode="ja",
    TargetLanguageCode="en",
)

print(result["TranslatedText"])
"""
Melos got furious.
He determined that he must exclude the king of wicked violence.
Melos does not know politics.
Melos is a village shepherd.
I played a whistle and played with the sheep.
However, for evil, people were more sensitive to evil.
Today, the unknown Melos left the village, crossed the field over the mountains, and came to this city of Silax, where the city of Silax is gone.
Melos has no father or mother.
There is no wife.
I live two with sixteen shy sisters.
"""

あっという間にできましたね。
「笛を吹き、羊と遊んで暮して来た。」の部分の主語が「I(私)」になってしまっていたり、邪悪に対して敏感なのがメロスではなく人々(people)になっていたり、
元々の文の主語が省略されていた部分については少し誤訳があるように感じますが、翻訳サービスとしてはやむを得ない部分もあるでしょう。

今回のコードでは、元の言語をSourceLanguageCode="ja"と指定しましたが、ここは”auto”とすることもできます。


result = client.translate_text(
    Text=text,
    SourceLanguageCode="auto",
    TargetLanguageCode="en",
)

この場合、 Amazon Comprehend を使って自動的に元のテキストの言語を推定して翻訳してくれます。
元の言語を何と推定したかは、結果のオブジェクトから取得できます。きちんと日本語(ja)になったようです。


print(result["SourceLanguageCode"])
"ja"

一度に翻訳できるテキストの長さは、5000byteまでです。5000文字ではないので特に僕ら日本人は注意が必要です。
試しに、元のテキスト(191文字)を20回繰り返して長文を作って翻訳にかけてみます。


long_text = text*20
print(len(long_text))
# 3820

try:
    result = client.translate_text(
        Text=long_text,
        SourceLanguageCode="ja",
        TargetLanguageCode="en",
    )
except client.exceptions.TextSizeLimitExceededException as e:
    print(e)

"""
An error occurred (TextSizeLimitExceededException) when calling the TranslateText operation:
Input text size exceeds limit.
Max length of request text allowed is 5000 bytes while in this request the text size is 11020 bytes
"""

出力されたエラーメッセージを読んでいただけるとわかりますが、5000バイトが上限なのに、11020バイト渡されたと言うエラーになっていますね。
しかし、リクエストしたテキストは3820文字です。

明らかに短いテキストを翻訳にかける場合は問題ないですが、そうでなければ、
translate_text を呼び出す前にチェックを入れるか、
上のコードみたいに例外処理を加えた方が良いでしょう。

matplotlibでグラフ枠から見た指定の位置にテキストを挿入する

matplotlibでは、 matplotlib.axes.Axes.text メソッドを指定して、グラフ中にテキストを挿入できます。
このとき、通常は、 ax.text({x座標}, {y座標}, {挿入したいテキスト}) という構文でテキストが入れられます。
ここで言う、 x座標/ y座標は それぞれx軸、y軸に対応した座標になります。当然ですね。
散布図の点や、グラフの頂点に文章を補足するときはとてもありがたい仕様ですが、
その反面、グラフの左上の方、とか中央といった、グラフの中の特定の場所にテキストを入れたい場合はちょと不便です。
それは、グラフの枠の左上や中央の座標が何になるか非自明だからです。

このニーズに対応して、x軸、y軸の座標ではなく、グラフ内部の位置でテキストの位置を指定できることがわかったので紹介します。

ドキュメントの Examples のところに 例があるのですが、transform=ax.transAxesを指定すると、ax.textのx座標、y座標はx軸y軸とは関係なくなり、
グラフの枠の左下を(0, 0), 右上を(1, 1)とする相対座標に変わります。

これを使って、グラフのすみの方や中央に簡単にテキストを置けます。

ちなみに、デフォルトでは、指定した座標の位置に、テキストの左下が当たるように配置されます。
これは、縦位置は verticalalignment{‘center’, ‘top’, ‘bottom’, ‘baseline'(デフォルト), ‘center_baseline’}、
横位置はhorizontalalignment{‘center’, ‘right’, ‘left'(デフォルト)} という引数で変更することができます。
引数名が長いので、それぞれ、va, ha というエイリアスを用意してくれています。
これらの引数は、 transform=ax.transAxes を指定しない時も同じように使えるので覚えておきましょう。

あまり面白い例でなくて恐縮ですが、色々パターンを試したコードとその出力を置いておきます。


import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(-np.pi, np.pi, 101)
y = np.sin(x)


fig = plt.figure(facecolor="w")
ax = fig.add_subplot(1, 1, 1)
ax.plot(x, y)

# 通常は、指定した座標の位置にテキストが挿入される
ax.text(np.pi/2, 1, "$(\pi/2, 1)$")
ax.text(-np.pi/2, -1, "$(-\pi/2, 1)$")

# transform=ax.transAxes を指定すると、図形の枠を基準にした位置にテキストが挿入される
ax.text(0.01, 0.01, "左下", transform=ax.transAxes)
ax.text(0.01, 0.99, "左上", verticalalignment='top', transform=ax.transAxes)
ax.text(0.99, 0.01, "右下", horizontalalignment='right', transform=ax.transAxes)
ax.text(0.99, 0.99, "右上", va='top', ha='right', transform=ax.transAxes)
ax.text(0.5, 0.5, "中央", va='center', ha='center', transform=ax.transAxes)
# 0 ~ 1 の範囲を超えるとグラフの外にテキストを配置できる
ax.text(0.5, 1.05, "上部枠外", ha='center', transform=ax.transAxes)
ax.text(1.02, 0.5, "右側枠外", va='center', transform=ax.transAxes, rotation=270)

plt.show()

出力はこちらです。

transform 引数に他にどんな値を設定しうるのか探したところ、以下のページが見つかりました。
参考: Transformations Tutorial
ax.transData がデフォルトのデータにしがたった座標軸みたいですね。
ax.transAxes の他にも、fig.transFigureやfig.dpi_scale_trans を使って、図全体の中での相対位置で
テキストを配置することもできるようです。
fig.transFigure のほうが、左下が(0, 0)、右上が(1, 1) となる座標で、
fig.dpi_scale_trans はピクセル数を使った具体的な座標指定です。

matplotlibのグラフのx軸とy軸を反転する方法まとめ

先日、matplotlibで作った少し手の込んだグラフを、x軸とy軸を反転したくなることがありました。
少々手こずったのでその時調べた内容をまとめておきます。

matplotlibでは、グラフの種類によってx軸y軸の反転方法が異なります。
大きく分けて次の3パターンがあるようです。

1. x座標とy座標の引数の順番を入れ替えるだけ。
2. x軸とy軸を反転させたバージョンの別のメソッドが用意されている。
3. グラフを書くときに引数で向き(orientation)を指定する。

また、物によっては反転させることができないものもあるようです。

具体的に一つやってみます。
まずデータの準備です。


# 必要ライブラリのインポートとデータ生成
import matplotlib.pyplot as plt
from scipy.stats import beta
import numpy as np

beta_fz = beta(a=3, b=2)
data = beta_fz.rvs(100)
# 真の期待値
x_mean  = beta_fz.stats("m")

そして、ここで生成したデータを元に、以下のグラフと、そのxy軸反転版を書いてみました。
– データのヒストグラム
– 元の分布の確率密度関数
– 元の分布の期待値の位置を表す線
– 元の分布のデータの70%のデータが含まれる区間の塗り潰し


x = np.linspace(0, 1, 101)
x_clip = np.linspace(beta_fz.ppf(0.15), beta_fz.ppf(0.85), 101)

fig = plt.figure(figsize=(12, 6), facecolor="w")

ax = fig.add_subplot(1, 2, 1, title="元のグラフ")
ax.hist(data, density=True, alpha=0.3)
ax.plot(x, beta_fz.pdf(x))
ax.fill_between(x_clip, beta_fz.pdf(x_clip), alpha=0.3, color="orange")
ax.vlines(x_mean, ymin=0, ymax=beta_fz.pdf(x_mean))

ax = fig.add_subplot(1, 2, 2, title="x軸y軸反転したグラフ")
# ヒストグラムは orientation="horizontal" を指定する
ax.hist(data, density=True, orientation="horizontal",  alpha=0.3)
# plot は渡す引数の順番を入れ替えるだけ
ax.plot(beta_fz.pdf(x), x)
# fill_between に対しては、 fill_betweenx メソッドが用意されている
ax.fill_betweenx(x_clip, beta_fz.pdf(x_clip), alpha=0.3, color="orange")
# vlines に対しては、 hlines メソッドが用意されている
ax.hlines(x_mean, xmin=0, xmax=beta_fz.pdf(x_mean))

plt.show()

出力されたグラフが次です。

x軸とy軸が入れ替わりました。 コードをみていただくとそれぞれ反転方法が異なっていることがわかると思います。

x座標とy座標の引数の順番を入れ替えるだけのパターンには、
ax.plot(折れ線グラフ)やax.scatter(散布図)があります。

x軸とy軸を反転させたバージョンの別のメソッドが用意されているパターンには、
棒グラフであれば、ax.bar と ax.barh、
エリアの塗りつぶしであれば、ax.fill_between と ax.fill_betweenx、
縦線であれば、 ax.vlines と ax.hlines
などの組み合わせがあります。
ドキュメントのこちらのページで似てる名前のメソッドを探すと良いでしょう。

グラフを書くときに引数で向き(orientation)を指定するパターンは、
だいたい、ヒストグラム ax.hist だけと考えても良さそうです。
これはそれぞれのメソッドのドキュメントを読めば判断することができます。
(もしかしたら他にもあるかもしれませんが。)

一部のグラフには反転方法がわからないものもありましたが、実用上困ることはなさそうです。

wordpressの記事の投稿名の一覧を確認する

先日偶然見つけたのですが、このブログのある記事のURL(パーマリンク)に日本語の文字列が混ざっていました。
全部英数字とハイフンで統一するようにしているので、その記事については修正したのですが、他にも設定し忘れているのがないか不安になって調べたのでその方法のメモです。

正直、記事数はそんなに多くないので、日本語が混ざってないかどうか確認するだけであればURLの一覧さえ取得できれば目視で確認できます。
で、管理画面で探したのですが、「記事のURL一覧エクスポート」、みたいな機能は見つかりませんでした。
そう言うプラグインはあるらしいのですが、一回確認するだけのためにプラグインを入れるのも嫌なので、DBから直接取得することにしました。

このブログのパーマリンクは、
https://analytics-note.xyz/%category%/%postname%/
という構成で設定しているので、postname(投稿名)の一覧を取得すれば良いです。

まず、こちらの記事で書いた方法でDBにログインします。
参考: LightsailのMySQLに接続する

DB名の確認。


mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| bitnami_wordpress  |
| mysql              |
| performance_schema |
| sys                |
+--------------------+

この中で、bitnami_wordpressが目当てのDBなので切り替えます。


mysql> use bitnami_wordpress;
Database changed

wordpressのデータベース構造についてはこちらにドキュメントがあります。
参考: データベース構造 – WordPress Codex 日本語版

記事の情報は、 wp_posts というテーブルに入っています。
この中の、 post_type が、 ‘post’のものが記事です。
そして、 post_status が ‘publish’ のものが公開中の記事になります。
目当ての URLに使われる投稿名は、 post_name 列に含まれています。
あとは日本語のタイトルである、 post_title 列と一緒に表示するSQL を書いて実行すれば、投稿名の一覧が取得できます。


mysql> SELECT
    post_title,
    post_name
FROM
    wp_posts
WHERE
    post_type = 'post'
AND
    post_status = 'publish'
;

これで確認できました。

pyLDAvisを用いたトピックモデル(LDA)の可視化 (gensim編)

前回に引き続き、pyLDAvisを用いたトピックモデルの可視化です。
今回は gensim 編です。

gensim で学習したトピックモデルの可視化の使い方はこちらの notebook で紹介されています。

scikit-learn と違うのは、
インポートするモジュールが、pyLDAvis.gensimに代わり、
呼び出すメソッドがpyLDAvis.gensim.prepareになる点だけですね。
メソッドに渡すのは、 学習したモデル、コーパスのデータ(BOW形式)、コーパス作成に使った辞書 である点も同じです。

以前書いた、
gensimでトピックモデル(LDA)をやってみる
のコードで学習したモデルでやってみましょう。
(トピック数だけ20に増やして実行しました。)


import pyLDAvis
import pyLDAvis.gensim
pyLDAvis.enable_notebook()

pyLDAvis.gensim.prepare(lda, corpus, dictionary)

これで、sickit-learnの時と同様に結果を得ることができます。

これだけだと何も代わり映えしないので、せっかくなので、次元削減のメソッドを変更して結果をそれぞれみてみましょう。

次の3行のコードを実行した結果を1枚の画像にまとめたのが次の出力です。


pyLDAvis.gensim.prepare(lda, corpus, dictionary, mds="pcoa") # デフォルト
pyLDAvis.gensim.prepare(lda, corpus, dictionary, mds="mmds")
pyLDAvis.gensim.prepare(lda, corpus, dictionary, mds="tsne")

次元削減のアルゴリズムの特徴が出ていますね。

これはスクリーンショットなので動かせませんが、実際にjupyter notebookで表示したりHTMLに保存したものはグリグリ動かしてみることができるので是非試してみてください。

pyLDAvisを用いたトピックモデル(LDA)の可視化 (scikit-learn編)

久々にトピックモデルの記事です。
トピックモデルを実行した時、各トピックが結局何の話題で構成されているのかを確認するのは結構面倒です。
また、各トピック間の関係などもなかなか掴みづらく解釈しにくいところがあります。
この問題が完全に解決するわけではないのですが、
pyLDAvisと言うライブラリを使うと、各トピックを構築する単語や、トピック間の関係性を手軽に可視化することができます。
しかもインタラクティブな可視化になっており、ポチポチ動かせるので眺めてみるだけで楽しいです。

まずは、可視化するモデルがないことには始まらないので、いつものライブドアニュースコーパスでモデルを学習しておきます。
トピック数はいつもより大目に20としました。(雑ですみません。いつも通り、前処理も適当です。)


import pandas as pd
import MeCab
import re
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.decomposition import LatentDirichletAllocation

# データの読みこみ
df = pd.read_csv("./livedoor_news_corpus.csv")
# ユニコード正規化
df["text"] = df["text"].str.normalize("NFKC")
# アルファベットを小文字に統一
df["text"] = df["text"].str.lower()

# 分かち書きの中で使うオブジェクト生成
tagger = MeCab.Tagger("-d /usr/local/lib/mecab/dic/mecab-ipadic-neologd")
# ひらがなのみの文字列にマッチする正規表現
kana_re = re.compile("^[ぁ-ゖ]+$")


def mecab_tokenizer(text):
    # テキストを分かち書きする関数を準備する
    parsed_lines = tagger.parse(text).split("\n")[:-2]
    surfaces = [l.split('\t')[0] for l in parsed_lines]
    features = [l.split('\t')[1] for l in parsed_lines]
    # 原型を取得
    bases = [f.split(',')[6] for f in features]
    # 品詞を取得
    pos = [f.split(',')[0] for f in features]

    # 各単語を原型に変換する
    token_list = [b if b != '*' else s for s, b in zip(surfaces, bases)]

    # 名詞,動詞,形容詞のみに絞り込み
    target_pos = ["名詞", "動詞", "形容詞"]
    token_list = [t for t, p in zip(token_list, pos) if p in target_pos]
    # アルファベットを小文字に統一
    token_list = [t.lower() for t in token_list]
    # ひらがなのみの単語を除く
    token_list = [t for t in token_list if not kana_re.match(t)]
    # 数値を含む単語も除く
    token_list = [t for t in token_list if not re.match("\d", t)]
    return " ".join(token_list)


# 分かち書きしたデータを作成する
df["text_tokens"] = df.text.apply(mecab_tokenizer)

print(df["text_tokens"][:5])
"""
0    読売新聞 連載 直木賞 作家 角田光代 初 長編 サスペンス 八日目の蝉 檀れい 北乃きい ...
1    アンテナ 張る 生活 映画 おかえり、はやぶさ 公開 文部科学省 タイアップ 千代田区立神田...
2    全国ロードショー スティーブン・スピルバーグ 待望 監督 最新作 戦火の馬 アカデミー賞 有...
3    女優 香里奈 都内 行う 映画 ガール 公開 女子高生 限定 試写 会 サプライズ 出席 女...
4    東京都千代田区 内幸町 ホール 映画 キャプテン・アメリカ/ザ・ファースト・アベンジャー 公...
Name: text_tokens, dtype: object
"""

# テキストデータをBOW形式に変換する
tf_vectorizer = CountVectorizer(
    token_pattern='(?u)\\b\\w+\\b',
    max_df=0.90,
    min_df=10,
)
tf = tf_vectorizer.fit_transform(df["text_tokens"])

# LDAのモデル作成と学習
lda = LatentDirichletAllocation(n_components=20)
lda.fit(tf)

さて、この後が本番です。
pyLDAvisのドキュメントはこちらですが、
scikit-learnのLDAの可視化の方法はこっちのnotebookを参照すると良いです。
Jupyter Notebook Viewer pyLDAvis.sklearn

pyLDAvis.sklearn.prepareに、LDAのモデル、BOW(かtf-idf)の学習に用いたデータ、変換に用いた辞書を順番に渡すだけです。
jupyter notebook 上で結果を表示するためにpyLDAvis.enable_notebook()というメソッドも呼び出しています。


import pyLDAvis
import pyLDAvis.sklearn

# jupyter notebookで結果を表示するために必要
pyLDAvis.enable_notebook()

pyLDAvis.sklearn.prepare(
    lda, # LDAのモデル (LatentDirichletAllocation のインスタンス)
    tf, # BOWデータ (sparse matrix)
    tf_vectorizer, # CountVectorizer もしくは TfIdfVectorizer のインスタンス
)

実行すると、以下のような図が表示されます。blogに貼ったのは画像ですが、実際は左の散布図で注目するトピックを切り替えたり、
右側の単語一覧から単語を選択することで、各トピックにその単語がどれだけ含まれるかみることができます。

次のように、 prepare メソッドの戻り値を受け取っておいて、 pyLDAvis.save_htmlメソッドに渡すことで、htmlとして保存することもできます。
このブログにも貼っておくので、是非触ってみてください。

HTMLファイルへのリンク: ldavis-sample

7番目と19番目や、5番目と13番目のトピックが大きくかぶっていて、トピック数が少し多すぎたかなとか、そう言った情報も得られますし、
単語の一覧から各トピックが何の話題なのかざっくりとつかめますね。

表示する単語数は、 R(デフォルト30)と言う引数で指定できます。
また、左側のトピックの位置関係表示に使う次元削減方法は、mdsと言う引数で変更もできます。
指定できるのは現在のところ、pcoa, mmds, tsne の3種類です。

pytablewriterで、pandasのDataFrameからMarkdownの表を生成する

前回の記事に続いて、pytablewriterの機能の紹介です。
参考: pytablewriterでMarkdownの表を生成する

今回はpandasのDataFrameからMarkdownを生成します。
個人的には使う頻度が高いのはこちらです。

まずサンプルとなるデータを作っておきましょう。


import pandas as pd

df = pd.DataFrame(
    data=[[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]],
    columns=['col1', 'col2', 'col3'],
    index=["data1", "data2", "data3", "data4"]
)
print(df)
"""
       col1  col2  col3
data1     1     2     3
data2     4     5     6
data3     7     8     9
data4    10    11    12
"""

早速、このDataFrameをMarkdownに変更します。

利用するのは、 from_dataframe と言うメソッドです。
ドキュメント: 4.4.3. Using pandas DataFrame as tabular data source

次のコードの例のように、 from_dataframe() にDataFrameを渡し、
write_table()メソッドを呼び出すと標準出力にマークダウンが出力されます。


from pytablewriter import MarkdownTableWriter


writer = MarkdownTableWriter()
writer.from_dataframe(df)
writer.write_table()
"""
|col1|col2|col3|
|---:|---:|---:|
|   1|   2|   3|
|   4|   5|   6|
|   7|   8|   9|
|  10|  11|  12|
"""

配列から生成するときはメソッドではなく属性にデータを代入していたのでちょっと使い方が違いますね。

さて、結果をご覧の通り、デフォルトではindexは出力されません。
もしindexが必要な場合は、 from_dataframe()を呼び出す時に、 add_index_column=True を指定する必要があります。


writer = MarkdownTableWriter()
writer.from_dataframe(df, add_index_column=True)
writer.write_table()
"""
|     |col1|col2|col3|
|-----|---:|---:|---:|
|data1|   1|   2|   3|
|data2|   4|   5|   6|
|data3|   7|   8|   9|
|data4|  10|  11|  12|
"""

これでindexも出力されました。

pytablewriterでMarkdownの表を生成する

集計結果を社内に共有する時など、Markdownでテーブルを書く機会はそこそこ頻繁にあります。
(実はBacklogを使っているので、正式なMarkdownとは違うのですが似たようなものです。)

毎回、Vimで置換等を使って書いて、それを貼り付けたりとかしているのですが、
Pythonに pytablewriter という便利なライブラリがあるのを見つけたのでそれを紹介します。
自分の場合は pandas の DataFrameを変換することが多く、それ専用の関数もあるのですが、
まずはこの記事ではドキュメントの Basic usage に沿った基本的な使い方を紹介します。

ドキュメント: 4.1. Basic usage — pytablewriter

ドキュメントの例を参考に、少し書き換えたコードでやってみます。


from pytablewriter import MarkdownTableWriter

writer = MarkdownTableWriter()
writer.table_name = "zone"
writer.headers = ["zone_id", "country_code", "zone_name"]
writer.value_matrix = [
    ["1", "AD", "Europe/Andorra"],
    ["2", "AE", "Asia/Dubai"],
    ["3", "AF", "Asia/Kabul"],
    ["4", "AG", "America/Antigua"],
    ["5", "AI", "America/Anguilla"],
]

writer.write_table()
"""
# zone
|zone_id|country_code|   zone_name    |
|------:|------------|----------------|
|      1|AD          |Europe/Andorra  |
|      2|AE          |Asia/Dubai      |
|      3|AF          |Asia/Kabul      |
|      4|AG          |America/Antigua |
|      5|AI          |America/Anguilla|
"""

テーブル名、ヘッダー、データを順番に指定してあげて、
その後、write_tableを呼び出すとマークダウンのテキストを生成してくれますね。

カラム名は指定しないと、A,B,C,Dとアルファベットが割り当てられるようです。


writer = MarkdownTableWriter()
writer.value_matrix = [
    ["1", "AD", "Europe/Andorra"],
    ["2", "AE", "Asia/Dubai"],
    ["3", "AF", "Asia/Kabul"],
    ["4", "AG", "America/Antigua"],
    ["5", "AI", "America/Anguilla"],
]
writer.write_table()
"""
| A | B |       C        |
|--:|---|----------------|
|  1|AD |Europe/Andorra  |
|  2|AE |Asia/Dubai      |
|  3|AF |Asia/Kabul      |
|  4|AG |America/Antigua |
|  5|AI |America/Anguilla|
"""

引数で渡すのではなく、.value_matrixなどの変数にデータを渡すのが特徴的で、
ちょっと使い方に癖がありますが手軽にマークダウンを生成できるので便利ですね。

n進法(特に2進法,8進法,16進法)の文字列を10進法の数値に変換する

以前書いた記事の逆向きの操作が必要になったのでそのメモです。
参考: Pythonの数値を2進法、8進法、16進法の表記に変換する

結論から言うと intメソッドでできます。
そのまま数字として解釈できる文字列(要するに0から9と+-)しか受け付けないと思い込んでいたのですが、
ドキュメントを読んでみると、2つ目の引数baseで、基数を指定できます。
ここに8なり16なりを渡してあげれば良いようです。


print(int("FF", 16))
# 255

print(int("100", 8))
# 64

base は 0と2~36までの引数を指定できます。(1はダメ)
36進法は 0~9までの数値と、a~z(A~Zでも可)の文字を使った表記ですね。


print(int("AZ09", 36))
# 511929

base=0 を指定した場合、文字列の先頭のプレフィックスを使って、2進法,8進法,10進法,16進法を判定するようです。


# 2進法
print(int("0b100", 0))
# 4

# 8進法
print(int("0o100", 0))
# 64

# 10進法
print(int("100", 0))
# 100

# 16進法
print(int("0x100", 0))
# 256

PythonでROT13暗号

業務で使う機会があったので紹介します。

非常に単純な暗号方式のに、シーザー暗号と呼ばれるものがあります。
参考: シーザー暗号 – Wikipedia

暗号鍵として整数nを決め、アルファベットをn文字ずらしたものに変換するものです。
$n=3$ であれば、aはdに、GはJに、Zはまた先頭に戻ってCに変換されます。
複合はその逆です。

この暗号鍵を13にしたものをROT13と呼びます。
参考: ROT13 – Wikipedia
アルファベットが大文字小文字それぞれ26文字であり、そのちょうど半分をスライドさせることから、
2回適用すると元に戻ると言う特徴があります。要するに暗号化と複合が全く同じと言う特徴があります。

これはPythonでは、 codecs という標準ライブラリの中に実装されています。
参考: codecs — codec レジストリと基底クラス — Python 3.9.1 ドキュメント

早速やってみます。


import codecs


text = "Hello World"
print(codecs.encode(text, "rot_13"))
# Uryyb Jbeyq

# もう一度適応すると元に戻る
print(codecs.encode("Uryyb Jbeyq", "rot_13"))
# Hello World

# encode と decodeの結果が等しい
print(codecs.decode(text, "rot_13"))
# Uryyb Jbeyq

簡単ですね。

コード中の “rot_13” ですが、 “rot13″というアンダーバー無しのエイリアスも用意されています。
そのため、以下のコードでも動きます。


print(codecs.encode(text, "rot13"))
# Uryyb Jbeyq

このROT13ですが、アルファベット以外の文字にには作用しません。数字や日本語の文字などはそのまま残ります。


print(codecs.decode("ROT13暗号はアルファベット以外そのまま", "rot13"))
# EBG13暗号はアルファベット以外そのまま