Python のインデントは,pithonista 以外には不評かも知れないし,不用意にインデントを壊すと目も当てられないことになるので,以下のようなコーディングを広めたい(のだ(^_^;))
for i in range(10): # {
if i % 2 == 0: # {
print(i, 'is even')
# }
else: # {
print(i, 'is odd')
# }
# }
冗談ですよ!!!
Python のインデントは,pithonista 以外には不評かも知れないし,不用意にインデントを壊すと目も当てられないことになるので,以下のようなコーディングを広めたい(のだ(^_^;))
for i in range(10): # {
if i % 2 == 0: # {
print(i, 'is even')
# }
else: # {
print(i, 'is odd')
# }
# }
冗談ですよ!!!
100000行1500変数のファイルから,第一列<=40,第二列>=60,第三列>=70 の条件を満たす行を抽出する。
tidyverse:::read_csv ... 遅い,遅すぎる
library(tidyverse)
system.time({
df <- read_csv("test.csv")
df <- df %>%
filter(X1 <= 40)%>% # X1 <= 40 の行を取り出す
filter(X2 >= 60) %>% # X2 >= 60 の行を取り出す
filter(X3 >= 70) # X3 >= 70 の行を取り出す
write_csv(df, "out.csv")
})
ユーザ システム 経過
39.960 1.574 42.804
AWK で以下のスクリプト
BEGIN{
FS = ","
getline
print $0
}
$1 <= 40 && $2 >= 60 && $3 >= 70
実行
$ time awk -f awk1.awk test.csv > out2.csv
0.571u 0.177s 0:00.75 98.6% 0+0k 0+0io 0pf+0w
AWK の方が,39.960 / 0.571 ≒ 70 倍速いです。
そりゃねえ,read_csv は,4列目から1500列目までの入力を文字列で入力して double で格納してるんだから,無理もないワ
私は,R と Python どちらが優れているかなんてことは言っていない(つもり)。
個人的に,単にどちらが好きかとか,どちらの書き方が好きかとか,どちらをよく使うかとかはいうけど。
○○を使うんなら,そんな書き方はないんじゃないの?とかもいうけど。
場合によって,あっちを使ったり,こっちを使ったり,別のものを使ったりするだけ。
コンピュータもコンピュータ言語も,ヒトも,ものは使いよう!!
Clear win for Python.
Personally, I really appreciate Python’s clean lines:
if x > y:
z = 5
w = 8
versus
if (x > y)
{
z = 5
w = 8
}
Python class structure cleaner than the various R structures.
ちなみに,後者は普通
if (x > y) {
z = 5
w = 8
}
と書く。
両者を比べて,Python は
if 文の条件式を ( ) でくくらないだけ(くくってもいいんだよ!)
if 文の最後が { でなく : じゃなきゃならない
if ブロックは,必ずインデントしなければならない
R に限らず,普通のプログラミング言語の記述で,インデントしないなんて,キチガイザタ
if ブロックの最後の } はいらない(あってはならないが)
という違いしかない。
「読みやすい」と言うが,それは「慣れ以外の何物でもない」
それに反して,メリットはほとんどない
メンテナンスやプログラム拡張で,インデントを不用意に壊すとバグの元
あれこれ勘案して,記述方法 { } かインデントかの違いは,どうでもいいこと。
https://github.com/matloff/TidyverseSkeptic
https://github.com/matloff/TidyverseSkeptic/blob/master/READMEFull.md
を読ませてもらいました。
全く同感ですね。
我々が,コンピュータを使って統計解析するとき,例えば,(十分複雑な条件づきで)
一番簡単には,ファイルの何列目にある変数を従属変数にして,何列目....を独立変数にして重回帰分析して!
という。
そもそも,どこにあるファイル?
ファイルにあるデータ全部使うの?
それぞれの変数は,数値型なの?
欠損値はあるの?
あったら,どうすればいいの?
なんてレベルの話ではなくて,
え,必要かも知れない相関係数行列ってどうやって求めるの?
まずね,それぞれの変数についてね,
データの個数を求めて,
データの合計値を求めて,
割れば,平均値になるでしょ?
でね,もとの変数から平均値を引いて,二乗してね,和を求めてね,それをデータの個数で割れば,ぶんさんになるでしょ,
そんなことをね,変数の組み合わせについておこなって,分散・共分散行列を求める訳よ。
そしてね,正規方程式の解を求めると,それが回帰係数になるわけよ。
予測値はその回帰係数から計算できるから,
予測誤差を求めて,ああだこうだすれば,決定係数も求まる訳よ。
というような,各段階で「最適な関数」をパイプでつないで最終解を得る。
というのが dplyr なんかな?
これを全て,最適な関数でつなげるのは,ニンゲンのやることじゃないよね。
ニンゲンは「○○という変数を予測するにはどうすればよいか?」だけでいいんじゃない。
例えば,上に上げた仕事をする最適な関数が以下のような関数の適切な組み合わせで行われるとする。
有効なサンプルの集合を作る
変数のサンプルの個数を数える
変数の和を求める
変数の平均値を求める
変数の不偏分散を求める
変数の標準偏差を求める
二変数の共変動を求める
行列とベクトルの積和を求める
行列積を求める
行列の逆行列を求める
連立方程式の解を求める
dplyr はこれらの関数の選択と適用順序を明示的に示すということに相当するとのだと思う。
そんなの,ニンゲンのやることじゃないよね。
もう,これだけからして,tidyverse を学ぼうという意欲は減退する(わらわら)。英語,得意じゃないからね。
コンパイラ言語とマシン語の関係から言えば,
より一般的な記述から特殊な(コンピュータが実行可能な)言語に翻訳して,コンピュータを動かして結果を得る。
つまり,
「データファイルの3列目の測定値について平均値を求めてくれ」ということをコンピュータが理解してくれるようにに伝える必要はある。
mean(data_frame[3]) が一番簡単だとして,ああだこうだと理屈をつけて,それよりも複雑な命令を与えないといけないとすれば,「それはちょっとちがうだろ?」ということでしょうかねえ?
def leap_year(year):
if year%100 == 0 and year%400 != 0:
return False
else:
if year%4 == 0:
return True
else:
return False
最後の 5 行は冗長
def leap_year(year):
if year%100 == 0 and year%400 != 0:
return False
return year%4 == 0
でよい。
見通しがよくなったので,一行で書くなら以下のようになることがわかる
def leap_year(year):
return False if year%100 == 0 and year%400 != 0 else year%4 == 0
Python では,ベクトルや配列を別の変数に代入しても,新しいメモリが確保されるのではなくもとのメモリが参照される。これはときどきやっかいなバグのもとになるが,便利に使うこともできる。
>>> import scipy as sp
>>> a = sp.arange(10)
>>> a
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
b, c に a の一部を代入する。
>>> b = a[:6]
>>> c = a[6:]
>>> b
array([0, 1, 2, 3, 4, 5])
>>> c
array([6, 7, 8, 9])
元の a を変化させると
>>> a *= 2
>>> a
array([ 0, 2, 4, 6, 8, 10, 12, 14, 16, 18])
b, c も変化する。b, c は別のメモリ上にあるのではなく,元の a の部分を指しているから当たり前。
>>> b
array([ 0, 2, 4, 6, 8, 10])
>>> c
array([12, 14, 16, 18])
b, c の要素を変化させると a も変わる
>>> c[2] = 200
>>> c
array([ 12, 14, 200, 18])
>>> a
array([ 0, 2, 4, 6, 8, 10, 12, 14, 200, 18])
しかし,以下のようにして c 全体を置き換えると,新たに c 用にメモリが確保されるようで,c は変わるが,a は不変だ。これも,使い方を間違えるとバグの元になるようだ。
>>> c = sp.repeat(20, 4)
>>> c
array([20, 20, 20, 20])
>>> a
array([ 0, 2, 4, 6, 8, 10, 12, 14, 200, 18])
# 成績判定のプログラム
def hyouka(score: int):
if score < 60 and score >= 0:
grade = 'C'
elif score < 80 and score >= 60:
grade = 'B'
elif score <= 100 and score >= 80:
grade = 'A'
return grade
というプログラム例があった。
お手本としてはどうかなあ?
論理式の書き順が素直でない。
def hyouka(score: int):
if score >= 0 and score < 60: # 普通,「スコアが 0 以上,60未満」て言うよね
grade = 'C'
elif score >= 60 and score < 80:
grade = 'B'
elif score >= 80 and score <= 100 :
grade = 'A'
return grade
数学的な順序で書くと,
def hyouka(score: int):
if 0 <= score and score < 60: # 0 ≦ score < 60 の順だなあ
grade = 'C'
elif 60 <= score and score < 80:
grade = 'B'
elif 80 <= score and score <= 100 :
grade = 'A'
return grade
Python 特有の数学的な書き方,
def hyouka(score: int):
if 0 <= score < 60: # 0 ≦ score < 60 そのままだなあ
grade = 'C'
elif 60 <= score < 80:
grade = 'B'
elif 80 <= score <= 100 :
grade = 'A'
return grade
結果をそれぞれ grade に代入して最後に return で返す必要もないし
def hyouka(score: int):
if 0 <= score < 60: # 0 ≦ score < 60 そのままだ
return 'C'
elif 60 <= score < 80:
return 'B'
elif 80 <= score <= 100 :
return 'A'
区間は連続しているんだから,「○○以上,△△未満」って書く必要ないよ
エラーも考えよう
def hyouka(score: int):
if score < 0:
return 'Error'
elif score < 60:
return 'C'
elif score < 80:
return 'B'
elif score <= 100 :
return 'A'
else:
return 'Error'
引数のアノテーションするなら,戻り値もアノテーションしよう
def hyouka(score: int) -> str:
if score < 0:
return 'Error'
elif score < 60:
return 'C'
elif score < 80:
return 'B'
elif score <= 100 :
return 'A'
else:
return 'Error'
hyouka(65.2) # 'B' を返す
仮引数のアノテーションは注釈のみ。実引数が違反していてもエラーにならない
ちゃんとエラー処理をしよう
def hyouka(score: int) -> str:
if score != int(score) or score < 0 or score > 100:
return 'Error'
elif score < 60:
return 'C'
elif score < 80:
return 'B'
else:
return 'A'
hyouka(65.2) # 'Error' を返す