カテゴリー: Python

gettextとは

Gettextは、国際化(internationalization(I18N)) と地域化(localization(L10N))のためのライブラリです。

目標

gettextとは何か、どのように使用するかを理解する。

gettextとは何か

gettextはソフトウェアの翻訳のためのライブラリです。PythonやC/C++, Java, PHP, JavaScriptで使用するソフトウェア中の文章を多言語対応させることができます。

pot ファイル

potファイルは、翻訳の為のテンプレートファイルです。このファイルで、翻訳する文章を定義しています。

po ファイル

potフォイルで定義された文章リストを各言語で翻訳したものが、poファイルです。

mo ファイル

moファイルは、poファイルから生成されたバイナリファイルです。

domain(ドメイン)

ドメインは、gettextが翻訳のテンプレをみつけるための識別子であり、固有の名前をつけます。

Pythonにおけるgettextの使い方

gettext モジュールを使用します。

方法1. GNU gettext API

この方法は、LinuxまたはUnixのみで使用することができます。なぜなら、この方法では、環境変数LANGUAGE, LC_ALL, LC_MESSAGESを使用している為です。
参照: GNU gettext API

1. ソースファイル”gettext_test.py”を作成します。

bindtextdomain()の引数’myapplication’がドメインとなります。’locale_dir’は、moファイルのあるディレクトリへのパスです。

import gettext

gettext.bindtextdomain('myapplication', 'locale_dir')
gettext.textdomain('myapplication')
_ = gettext.gettext
print (_('Hello World!'))

2. Python<バージョン>(Python36など)/Tools/i18nというディレクトリにあるpygettext.pyを使って、<ドメイン名>.potファイルを作成します。potファイルmyapplication.pot が指定した”locale_dir”の中に作成されます。

python Python<version>/Tools/i18n/pygettext.py -d myapplication -p locale_dir gettext_test.py

以下が生成されたmyapplication.potです。

# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR ORGANIZATION
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.

msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSIONn"
"POT-Creation-Date: 2020-07-20 01:35+0900n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONEn"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>n"
"Language-Team: LANGUAGE <LL@li.org>n"
"MIME-Version: 1.0n"
"Content-Type: text/plain; charset=cp932n"
"Content-Transfer-Encoding: 8bitn"
"Generated-By: pygettext.py 1.5n"

#: gettext_test.py:7
msgid "Hello World!"
msgstr ""

3. poファイルを作成し、languageディレクトリに入れます。

potファイルのcharsetをutf-8に変更し、翻訳を入力してmyapplication.poという名前をつけて保存します。今回の例では、日本語の”こんにちは世界!”をmsgstrに入力しました。

# SOME DESCRIPTIVE TITLE.
.
.
.
"Content-Type: text/plain; charset=utf-8n"
.
.
.
#: gettext_test.py:7
msgid "Hello World!"
msgstr "こんにちは世界!"

作成したmyapplication.poを<指定したディレクトリ>/language/LC_MESSAGESというディレクトリの中に入れます。

languageというのは、<language codes>_<country codes>で指定されます。今回は日本語なので、locale_dir/ja_JP/LC_MESSAGESとなります。
language codeとcountry codeのリストはこちらのリンクにあります: ISO-3166 Country Codes and ISO-639 Language Codes

4. poファイルからmoファイルを生成します。Python<バージョン>(Python36など)/Tools/i18n/nmsgfmt.pyを使用します。

python Python<version>/Tools/i18n/msgfmt.py myapplication.p

以下が、最終的なファイル構造となります。

5. gettext_test.pyを実行します

python gettext_test.py
こんにちは世界!

方法2. Class-based API

この方法は、LinuxとUnix、Windowsで使用することができます。
参照: Class-based API

1. “gettext_test.py”を作成する

この方法では、translation()を使用します。引数’myapplication’がドメインであり、’locale_dir’がmoファイルの存在するディレクトリです。

import os
import gettext

_ = gettext.translation(
    domain='myapplication',
    localedir = 'locale_dir',
    languages=['ja_JP'], 
    fallback=True).gettext

print (_('Hello World!'))

残りの処理は、上記の”Method 1. GNU gettext API”と同じです。

2. pygettext.pyを使って<domain>.potを作成します

3. poファイルを作成し、languageディレクトリ<ディレクトリ名>/language/LC_MESSAGESに保存します。

4. Python<バージョン>/Tools/i18n/msgfmt.pyを使って、poファイルからmoファイルを生成します

5. gettext_test.pyを実行します。

python gettext_test.py
こんにちは世界!

Pythonによる対応ありone-way ANOVA(一元配置分散分析)と多重比較

目標

対応ありone-way ANOVA(一元配置分散分析)と多重比較のプログラムをPythonで書く。対応無しのone-way ANOVAについては、 Unpaired One-way ANOVA And Multiple Comparisons In Python“を参照してください。

ANOVAとは

ANOVA(analysis of variance)は、サンプル内のグループの平均値の差について、要因間で優位な差があるかどうかを確かめる統計的仮設検定の方法です。
内容の詳細については割愛します。下記サイトなどをご覧ください。

one-way ANOVA(一元配置分散分析)とは、3つ以上のサンプルの平均を比較する分散分析テストです。 帰無仮説は、全てのグループのサンプルが同じ平均の母集団から取られたというものです。

対応のある分散分析とは、同じ被験者に対して繰り返し条件を変えて実験したような場合のデータを扱うことを意味します。

実装

以下は、対応のあるone-way ANOVAの実装例です。

ライブラリのインポート

ANOVAの検定をおこなうために以下のライブラリをインポートします。

import statsmodels.api as sm
from statsmodels.formula.api import ols
import pandas as pd
import numpy as np
import statsmodels.stats.anova as anova

データの用意

以下のようなデータを使用します。id_nは被験者であり、条件はA、B、Cの3つです。

id_1id_2id_3id_4id_5id_6id_7
condition A85908869789887
condition B55826764785449
condition C46955980527370

test_data.csvという名前で以下のようなファイルを作成します。

85, 90, 88, 69, 78, 98, 87
55, 82, 67, 64, 78, 54, 49
46, 95, 59, 80, 52, 73, 70

データの読み込みと格納

open(‘file_name’, ) as f: でファイルを読み込みます。fを区切って各数値をitemsに格納していきます。各行について、itemsを取得したら、csv_lines[0], csv_lines[1], csv_lines[2]に格納します。

csv_line = []
with open('test_data.csv', ) as f:
    for i in f:
        items = i.split(',')
        for j in range(len(items)):
            if 'n' in items[j]:
                items[j] = float(items[j][:-1])
            else:
                items[j] =float(items[j])
        print(items)
        csv_line.append(items)

各行のデータにgroupA, groupB, groupCと名付けました。(csv_lineに入れずに直接groupA, groupB, groupCに入れることもできますが、自分が分かりやすくするためこのようにしました)

ラベルを付けて、各グループのデータをセットし、pd.DataFrameを作成。

groupA = csv_line [0]
groupB = csv_line [1]
groupC = csv_line [2]
tdata = pd.DataFrame({'A':groupA, 'B':groupB, 'C':groupC})
tdata.index = range(1,10)
tdata 

tdataは以下のようになります。

もしも、平均や分散などデータの統計的値を見たい場合は、DataFrame.describe()を実行.

tdata.describe()

ANOVAの実行

得点、条件、被験者のデータからpd.DataFrameを作成する。

  • subjects: ‘id1’, ‘id2’, ‘id3’, ‘id4’, ‘id5’, ‘id6’, ‘id7’, ‘id1’, ‘id2’, ‘id3’, ‘id4’, ‘id5’, ‘id6’, ‘id7’, ‘id1’, ‘id2’, ‘id3’, ‘id4’, ‘id5’, ‘id6’, ‘id7’
  • points: groupA + groupB + groupC = 85, 90, 88, 69, 78, 98, 87, 55, 82, 67, 64, 78, 54, 49, 46, 95, 59, 80, 52, 73, 70.
  • conditions: ‘A’, ‘A’, ‘A’, ‘A’, ‘A’, ‘A’, ‘A’, ‘B’, ‘B’, ‘B’, ‘B’, ‘B’, ‘B’, ‘B’, ‘C’, ‘C’, ‘C’, ‘C’, ‘C’, ‘C’, ‘C’
subjects=['id1','id2','id3','id4','id5','id6','id7']
points = np.array(group0 +group1 + group2)
conditions = np.repeat(['A','B','C'],len(groupA))
subjects = np.array(subjects+subjects+subjects)
df = pd.DataFrame({'Point':points,'Conditions':conditions,'Subjects':subjects})

anova.AnovaRM(df, ‘Point’, ‘Subjects’, [‘Conditions’])でANOVAを実行。

aov=anova.AnovaRM(df, 'Point','Subjects',['Conditions'])
result=aov.fit()

print(result)

>>                 Anova
========================================
           F Value Num DF  Den DF Pr > F
----------------------------------------
Conditions  5.4182 2.0000 12.0000 0.0211
========================================

p値が小さいほど、帰無仮説が棄却されるようになる。統計的に優位な場合、つまりp値が0.05未満(通常≤0.05)の場合は、多重比較を実行します。 このp値は、対応のある分散分析と対応のない分散分析で異なります。

Tukeyの多重比較

テューキーの範囲検定(tuky’s HSD, honestly significant difference test)を行うには、pairwise_tukeyhsd(endog, groups, alpha=0.05)を使用します。引数endogは従属変数のデータの配列(A[0] A[1]… A[6] B[1] … B[6] C[1] … C[6])です。引数groupsは、従属変数に対応する名前(A、A…A、B…B、C…C)のリストです。alphaは有意水準です。

def tukey_hsd(group_names , *args ):
    endog = np.hstack(args)
    groups_list = []
    for i in range(len(args)):
        for j in range(len(args[i])):
            groups_list.append(group_names[i])
    groups = np.array(groups_list)
    res = pairwise_tukeyhsd(endog, groups)
    print (res.pvalues) #print only p-value
    print(res) #print result

結果の出力

次の場合は、Group AとGroup Bの平均に有意差があるという結果になります。

print(tukey_hsd(['A', 'B', 'C'], tdata['A'], tdata['B'],tdata['C']))
>> [0.02259466 0.06511251 0.85313142]
 Multiple Comparison of Means - Tukey HSD, FWER=0.05 
=====================================================
group1 group2 meandiff p-adj   lower    upper  reject
-----------------------------------------------------
     A      B -20.8571 0.0226 -38.9533 -2.7609   True
     A      C -17.1429 0.0651 -35.2391  0.9533  False
     B      C   3.7143 0.8531 -14.3819 21.8105  False
-----------------------------------------------------
None

Pythonのgetattr()とは

目標

Pythonのgetattr()関数について理解する。

getattr(Object, name [, default])

Return the value of the named attribute of objectname must be a string. If the string is the name of one of the object’s attributes, the result is the value of that attribute. For example, getattr(x, 'foobar') is equivalent to x.foobar. If the named attribute does not exist, default is returned if provided, otherwise AttributeError is raised.

Python document > library > functions
  • この関数は、オブジェクトの指定した名前(name)の属性の値を返します。
    • このnameは文字列でなければなりません。
  • 例えば、getattr(x, ‘foobar’)はx.foobarと同じ値を返します。
  • 指定された属性が存在しない場合は、defaultが用意されていればその値を、それ以外の場合はAttributeErrorが返されます。

実行例

サンプルコードです。Dogクラスは、name, ageというインスタンス変数を持ち、sit(), bark()というインスタンスメソッドを持ちます。

class Dog():
    def __init__(self, name, age):
        self.name = name
        self.age = age
    def sit(self):
        self.tired = False
        print("sit down")
    def bark(self, count):
        self.tired = True
        for i in range(count):
            print("bow wow")

dog1 = Dog("Poppy", 3)
val1 = getattr(dog1, "name")
val2 = getattr(dog1, "age")
val3 = getattr(dog1, "tired", "unknown")
val4 = getattr(dog1, "sit")()
val5 = getattr(dog1, "bark")(2)
val6 = getattr(dog1, "sit")
val7 = getattr(dog1, "bark")
val8 = getattr(dog1, "tired", "unknown")

属性の取得

インスタンス名と属性名を引数に与えることで インスタンス変数の値を取得できます。

# val1 = getattr(dog1, "name")
print(val1)
>> Poppy
# val2 = getattr(dog1, "age")
print(val2)
>> 3

Get default value

存在しないメソッドや変数を取得し用とした場合は、第三引数(default)に指定した値を返す。

# val3 = getattr(dog1, "tired", "unknown")
print(val3)
>> unknown

インスタンスメソッドの実行

インスタンスメソッドの実行、

# val4 = getattr(dog1, "sit")()
print(val4)
>> None
# val5 = getattr(dog1, "bark")(2)
print(val5)
>> None

インスタンスメソッドの取得

インスタンスメソッドを取得し、()を付けることで、メソッドを実行することができます。

# val6 = getattr(dog1, "sit")
print(val6)
>> <bound method Dog.sit of <__main__.Dog object at 0x7f192a93f1d0>>
val6()
>> sit down

取得したメソッドに引数を渡すこともできます。

# val7 = getattr(dog1, "bark")
print(val7)
>> <bound method Dog.bark of <__main__.Dog object at 0x7fb2169ae1d0>>
val7(3)
>> bow wow
>> bow wow
>> bow wow

メソッドを実行した後の変化

# val8 = getattr(dog1, "tired", "unknown")
print(val8)
>> True

先ほどはunknownだったインスタンス変数tiredの値が、getattr(dog1, bark)(2) によってTrueに変化しました。



GOAL

To understand getattr() function in python.

getattr(Object, name [, default])

Return the value of the named attribute of objectname must be a string. If the string is the name of one of the object’s attributes, the result is the value of that attribute. For example, getattr(x, 'foobar') is equivalent to x.foobar. If the named attribute does not exist, default is returned if provided, otherwise AttributeError is raised.

Python document > library > functions

What will happen

class Dog():
    def __init__(self, name, age):
        self.name = name
        self.age = age
    def sit(self):
        self.tired = False
        print("sit down")
    def bark(self, count):
        self.tired = True
        for i in range(count):
            print("bow wow")

dog1 = Dog("Poppy", 3)
val1 = getattr(dog1, "name")
val2 = getattr(dog1, "age")
val3 = getattr(dog1, "tired", "unknown")
val4 = getattr(dog1, "sit")()
val5 = getattr(dog1, "bark")(2)
val6 = getattr(dog1, "sit")
val7 = getattr(dog1, "bark")
val8 = getattr(dog1, "tired", "unknown")

Get attribute

# val1 = getattr(dog1, "name")
print(val1)
>> Poppy
# val2 = getattr(dog1, "age")
print(val2)
>> 3

Get default value

# val3 = getattr(dog1, "tired", "unknown")
print(val3)
>> unknown

Execute function

# val4 = getattr(dog1, "sit")()
print(val4)
>> None
# val5 = getattr(dog1, "bark")(2)
print(val5)
>> None

Get function

# val6 = getattr(dog1, "sit")
print(val6)
>> <bound method Dog.sit of <__main__.Dog object at 0x7f192a93f1d0>>
val6()
>> sit down
# val7 = getattr(dog1, "bark")
print(val7)
>> <bound method Dog.bark of <__main__.Dog object at 0x7fb2169ae1d0>>
val7(3)
>> bow wow
>> bow wow
>> bow wow

After Executing function

# val8 = getattr(dog1, "tired", "unknown")
print(val8)
>> True

getattr(dog1, sit)() and getattr(dog1, bark)(2) changed internal variable “tired”.