読者です 読者をやめる 読者になる 読者になる

無粋な日々に

日々の技術メモ(データ分析界隈)

Python: 多次元のnumpy.arrayを1次元にするreshapeとravel

numpyの多次元配列を一次元配列にしたいときは、reshape(-1,)ravel()を使えば良い

import numpy as np

# サンプルデータ生成(3 × 3)
> x = np.arange(9).reshape(3, 3)

# 表示
> x

array([[0, 1, 2],
       [3, 4, 5],
       [6, 7, 8]])

# 方法1
> x.reshape(-1,)

array([0, 1, 2, 3, 4, 5, 6, 7, 8])


# 方法2
> np.ravel(x)

array([0, 1, 2, 3, 4, 5, 6, 7, 8])

ねむい。ごきげんよう

Python: print関数の仕様

print関数って、意識せず使ってたけどこんな仕様なんですねぇ 面白かったので、いくつか見てみましょう。

複数引数がとれて、一気にprintできる

複数引数を渡すと、スペースで区切られて出力される

# 複数引数を与えてみる
print('str1', 'str2', 'str3')

#> str1 str2 str3

セパレタも選択できる

複数引数を出力する際のデフォルトのセパレタはスペースですが、これも変えられる これって、Rでいうpasteとprintを両方兼ね添えた感じですね

# セパレタを変えてみる
print('str1', 'str2', 'str3', sep=':')

#> str1:str2:str3

ってことはlistをアンパックしたりすればこんな事もできますね

# リストをアンパックして渡して、全部の要素を接続
x = ['a', 'b', 'c']
print(*x, sep='')

#> abc

引数渡さなくてもいい(笑)

何も引数を渡さないと、end仮引数でデフォルトで指定されている\n改行が出力される。 endを指定することで、第1引数の出力後のお尻を何で締めくくるか決めれるみたい

# エラーにはならず、改行が出力(見えんけど)
print()

#>

# end引数に値を渡すと、出力のお尻を何で締めくくるか決められる
print('str1', end='end')

#> str1end

出力先も変えられる

標準出力(sys.stdout)以外にもwrite(string) メソッドを持つオブジェクトならなんでも出力できるみたいです。 例えば普通にファイルにも出力できる

# out.txtファイルに`str`が出力されます
with open('out.txt', 'w') as f:
    print('str', file=f)

flushオプションはわからん

バッファするかどうか、指定するみたいですが私にはわからなかったのでどう使うのか、誰か教えてください。

ドキュメントに書いてること

2. 組み込み関数 print

print(*objects, sep=‘ ’, end=‘\n’, file=sys.stdout, flush=False)
objects (単数でも複数でも可) を sep で区切りながらテキストストリーム file に表示し、最後に end を表示します。sep、end、file を与える場合、キーワード引数として与える必要があります。 キーワードなしの引数はすべて、 str() がするように文字列に変換され、 sep で区切られながらストリームに書き出され、最後に end が続きます。 sep と end の両方とも、文字列でなければなりません。これらを None にすると、デフォルトの値が使われます。 objects が与えられなければ、 print() は end だけを書き出します。 file 引数は、 write(string) メソッドを持つオブジェクトでなければなりません。指定されないか、 None である場合、 sys.stdout が使われます。表示される引数は全てテキスト文字列に変換されますから、 print() はバイナリモードファイルオブジェクトには使用できません。代わりに file.write(…) を使ってください。 出力がバッファ化されるかどうかは通常 file で決まりますが、flush キーワード引数が真ならストリームは強制的にフラッシュされます。 バージョン 3.3 で変更: キーワード引数 flush が追加されました。

ではごきげんよう。

pandas.DataFrameのplotメソッドとsubplotを使って、複数グラフを柔軟にプロットする

pandasのデータフレームのplotメソッドは超便利でよくお世話になる。 複数カラムを別々のグラフに出力したい場合もsubplot=Trueを指定するだけいいので結構程度便利なんだけど、 もう少し柔軟にプロットしたい時がある。

例えば、

  • カラム1とカラム2は1つ目のグラフに表示、カラム3とカラム4は2つ目のグラフに表示したい
  • グラフごとにタイトル変えたい
  • 軸をグラフごとに柔軟に変更したい

こういうときは、pyplotのsubplotとpandas.DataFrame.plotのあわせ技でプロットすると良いんではないだろうか 例えば以下のような感じ。あとは必要に応じて軸のformatterとか個別に設定するば良いんじゃないかと

Python: jsonな文字列に対してformatメソッドを使う

jsonな文字列に対してformatメソッドを使う方法

pythonjson形式を含む文字列をformatメソッドを使う場合ちょっと工夫が必要だ。 例えば、以下のような文字列に対してformatメソッドを使うとエラーが出る

# jsonな文字列
json_str = """\
{'member':'{name}', 'age': 26}
"""

# formatメソッドで値を指定しようとすると
prof = json_str.format(name='john')

# エラーが出る
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
<ipython-input-44-686c908f6197> in <module>()
----> 1 json_str.format(**{name='john'})

KeyError: "'member'"

これはプレースホルダのkeyとjsonのフォーマットのkeyをformatメソッドが区別できないかららしい。以下のように文字列中のjson用の{}を二重にしてあげるとエラーは出ない。(深い理由は知らないです。ご存知のかた教えてください)

# jsonな文字列。{}を2重にする
json_str = """\
{{'member':'{name}', 'age': 26}}
"""

prof = json_str.format(name='john')

# エラーは出ない。
print(eval(prof))

#> {'age': 26, 'member': 'john'}

json形式の文字列から、eval関数を使ってdict型のデータを生成することはよくあるので知っておくとよい。

参考

Python: 正規表現で複数行マッチングの置換を行う

Python正規表現で複数行マッチングの置換を行う

Pythonreモジュールを使って複数行に渡る正規表現マッチング・置換を行う場合はflagsオプションに適宜re.MULTILINEre.DOTALLを指定する。

# 直接置換する場合
re.sub(pattern, repl, string, flags=(re.MULTILINE | re.DOTALL))

# compileする場合
r = re.compile(pattern, flags=(re.MULTILINE | re.DOTALL))
re.sub(pattern, repl, string)

具体的な内容

^$を各行にマッチさせるre.MULTILINEフラグ指定

re.MULTILINEを指定しない場合、^は文字列の先頭に、$は文字列の末尾しかマッチしない。つまり1つの文字列変数につきそれぞれ1箇所しかマッチし得ない。なので改行を含む文字列で各行の 先頭や末尾(改行)にマッチさせたい場合はre.MULTILINEを指定する必要がある

例えば、以下の文字列(SQL)のコメント行を2行とも削除したい場合

# この文字列のコメント部分を削除したい
query = """\
-- this is 1st comment
-- this is 2nd comment
select * from table limit 10;
"""

re.MULITILINEフラグ指定がないと1行しか削除されません

# re.MULITILINE指定なしで削除
re.sub(r'^---*.*', '', query)

#>-- this is 2nd comment
#>select * from table limit 10;

re.MULITILINEフラグ指定を行うと2行とも削除されます

# re.MULITILINE指定して削除
re.sub(r'^\s*---*.*$', '', query, flags=re.MULTILINE)

#>select * from table limit 10;

ちなみにre.MULITILINEフラグ指定なしで正規表現$をパターンに入れると何も削除されません。マッチないからです。フラグ指定なしでは$文字列の末尾にしかマッチしません。行の末尾にはマッチしません。

# re.MULITILINE指定なしで、$を入れる
re.sub(r'^---*.*$', '', query)

#>-- this is 1st comment
#>-- this is 2nd comment
#>select * from table limit 10;

re.MULTILINE(原文) 指定されると、パターン文字 '^' は、文字列の先頭および各行の先頭(各改行の直後)とマッチします;そしてパターン文字 '$' は文字列の末尾および各行の末尾 (改行の直前) とマッチします。デフォルトでは、 '^' は、文字列の先頭とだけマッチし、 '$' は、文字列の末尾および文字列の末尾の改行の直前(がもしあれば)とマッチします。6.2. re — 正規表現操作

.を改行(\n)にもマッチさせるre.DOTALLフラグ指定

re.DOTALLフラグ指定が無いと.は改行にはマッチしません。re.DOTALLフラグ指定をすることで改行にもマッチします。

同様に以下の文字列(SQL)の/* ~ */コメント行のすべてを削除したい場合

query = """\
/*
this is 1st comment
this is 2nd comment
*/
select * from table limit 10;
"""

フラグ指定がないと削除されません

# re.MULITILINE指定なし
re.sub(r'/\*.*\*/', '', query)

#>/*
#>this is 1st comment
#>this is 2nd comment
#>*/
#>select * from table limit 10;

re.DOTALLフラグ指定を行うと4行とも削除されます

# re.MULITILINE指定
re.sub(r'/\*.*\*/', '', query, flags=re.DOTALL)

#>select * from table limit 10;

※ちなみにこれでコメント削除されるんですが、実際に上記のパターンをこのまま使うと、/*~*/が2箇所以上出てきた場合、コメントブロックに挟まれた途中のコードも全部消えるので注意が必要です(greedy matching; どう書くのが良いんでしょう。教えてください)

re.DOTALL¶(原文) 特殊文字 '.' を、改行を含む任意の文字と、とにかくマッチさせます;このフラグがなければ、 '.' は、改行 以外の 任意の文字とマッチします。6.2. re — 正規表現操作

フラグを両方指定する場合

フラグなのでORを取れば大丈夫です。

flags = (re.MULITILINE | re.DOTALL)

R: RのBroadcast

RのBroadcast

RのBroadcast(ベクトル、行列などの型や次元を自動で合わせて演算する)でたまに混乱するのでまとめる。結論だけ書くとRではベクトルを行列にBroadcastするとき、列方向で埋め合わせるという事だけを忘れないようにすればOK

基本原理

デフォルトでは列方向で要素を埋めていく

まず基本原理把握のため、ベクトルから行列を生成してみる。Rではベクトルから行列を生成する際、デフォルトでは列方向(bycol)優先にで要素を埋めていく。 例えば、\( (1, 2, \cdots ,20) \)のベクトルから、5x4行列を生成する過程を考える

# ベクトル生成
(v <- 1:20)

#> [1]  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20


# ベクトルから行列を生成 (何も指定しないと、列方向で要素が埋められていく)
(X <- matrix(v, nrow=4, ncol=5))

#>     [,1] [,2] [,3] [,4] [,5]
#>[1,]    1    5    9   13   17
#>[2,]    2    6   10   14   18
#>[3,]    3    7   11   15   19
#>[4,]    4    8   12   16   20

行方向で埋め合わせたい場合はbyrowオプションを入れる

要素を行方向で使う場合にはbyrow=TRUEを指定すれば良い

# ベクトルから行列を生成 (byrowオプションを指定することで要素を行方向で埋めることもできる)
(X <- matrix(v, nrow=4, ncol=5, byrow=TRUE))

#>    [,1] [,2] [,3] [,4] [,5]
#>[1,]    1    2    3    4    5
#>[2,]    6    7    8    9   10
#>[3,]   11   12   13   14   15
#>[4,]   16   17   18   19   20

デフォルトではベクトルから行列を生成する際、列方向に使うという事。この基本原理でBroadcastも行われるのだろうと想像できる

実際にBroadcastしてみる

実際にベクトルと行列を演算してBroadcastしてみる

# 4×5の0行列を生成
(A <- matrix(0, nrow=4, ncol=5))

#>    [,1] [,2] [,3] [,4] [,5]
#>[1,]    0    0    0    0    0
#>[2,]    0    0    0    0    0
#>[3,]    0    0    0    0    0
#>[4,]    0    0    0    0    0

# 行列とベクトルの足し算(element wise)でBroadcastしてみる
(A + v)

#>    [,1] [,2] [,3] [,4] [,5]
#>[1,]    1    5    9   13   17
#>[2,]    2    6   10   14   18
#>[3,]    3    7   11   15   19
#>[4,]    4    8   12   16   20

よくある危険な例(中心化する場合)

よくある処理の例で見ていきます。 行列を各変数(列)ごとに中心化してみましょう。よくやる \( {\bf X} - {\bf E}({\bf X}) \) です。

# 0-50のランダムな数で、5x5行列を生成
set.seed(0)
(X <- matrix(sample(0:50, 25, replace=TRUE), nrow=5, dimnames = list(NULL,paste0('x', as.character(1:5)))))


#>     x1 x2 x3 x4 x5
#>[1,] 45 10  3 39 39
#>[2,] 13 45 10 25 47
#>[3,] 18 48  9 36 10
#>[4,] 29 33 35 50 33
#>[5,] 46 32 19 19  6


# 列の平均値を取る
(X.center <- colMeans(X))

#>   x1   x2   x3   x4   x5 
#> 30.2 33.6 15.2 33.8 27.0 

ダメな例

もとの行列から平均値を引いていくのですが、そのまま引くと意図と異なる値が出ます。以下は間違いです

# 普通に引き算してしまうと列方向でBroadcastされるのでNG
(X - X.center)

#>        x1    x2    x3   x4    x5
#>[1,]  14.8 -20.2 -27.2  8.8   8.8
#>[2,] -20.6  11.4 -23.6 -8.6  13.4
#>[3,]   2.8  32.8  -6.2 20.8  -5.2
#>[4,]  -4.8  -0.8   1.2 16.2  -0.8
#>[5,]  19.0   5.0  -8.0 -8.0 -21.0

正しい例

正しくは行方向でBroadcastしてもらわないとダメです。例えば以下のような方法があります

# 1.明示的に行方向で行列に変換
(X - matrix(X.center, nrow=5, ncol=5, byrow=TRUE))

#>        x1    x2    x3    x4  x5
#>[1,]  14.8 -23.6 -12.2   5.2  12
#>[2,] -17.2  11.4  -5.2  -8.8  20
#>[3,] -12.2  14.4  -6.2   2.2 -17
#>[4,]  -1.2  -0.6  19.8  16.2   6
#>[5,]  15.8  -1.6   3.8 -14.8 -21

# 2. 一度転置(transpose)して、戻す
t(t(X) - X.center)

#>        x1    x2    x3    x4  x5
#>[1,]  14.8 -23.6 -12.2   5.2  12
#>[2,] -17.2  11.4  -5.2  -8.8  20
#>[3,] -12.2  14.4  -6.2   2.2 -17
#>[4,]  -1.2  -0.6  19.8  16.2   6
#>[5,]  15.8  -1.6   3.8 -14.8 -21

数式書くテスト

数式書くテスト

$$ \begin{equation} y=\theta_1x_1 \end{equation} $$

参考