#はじめに
日経 xTECH ビジネスAI Advent Calendar 2019
AI道場「Kaggle」への道の18日目を担当させていただきます。
過去の記事で、
kaggleに登録したら、まずcontributerを目指そう
なんて記事を書きましたが、kaggleって興味あるけど、そもそもpythonインストールしただけで何もしてないんだよね・・という方!
kaggleにはcoursesという導入を助けてくれるコーナーがあるのですよ!
導入も学べてコンペで技術も学べる!kaggleってすばらしい!
#英語なんだけど・・・
プログラミングとか機械学習界隈は英語ばかりなんです・・・
助けてg〇〇gle先生! でもいいのですが、今回は細かい部分にコメントを入れながらpython導入コースのさらに導入を担当しようかと。
#kaggle レッスン pythonについての導入
ColinMorrisさんに感謝
2019.12時点でのまとめ
私はpythonのドキュメントを読んでチュートリアル替わりにしましたが、今一度「タプル・リストとは?」や、「メソッド、importとは?」などに立ち返ることが出来て勉強になりました。
他人のまとめたものを見ると、自分の理解がさらに深まるので、pythonは知っているけど、始めたばかりの人、なんとなくの感覚で使っている人には復習になっていいと思います。
##全7回の講義。早速やっていこう
#1-1.hellow,python pythonを始める
python syntax(構文)を学ぶ
pythonはモンティパイソンというイギリスのコメディ番組の名前に由来している。
spamについてのコードを動かして、まずどんな結果が返ってくるのかを知ろう。
spam_amount = 0
print(spam_amount)
# Ordering Spam, egg, Spam, Spam, bacon and Spam (4 more servings of Spam)
spam_amount = spam_amount + 4
if spam_amount > 0:
print("But I don't want ANY spam!")
viking_song = "Spam " * spam_amount
print(viking_song)
0
But I don't want ANY spam!
Spam Spam Spam Spam
空いてないスパムがこんなに!
アホらしいコードですが、何をしているか見てみましょう。
ここで出てくるspamやvikingはpython言語とはあまり関係ありません。
バイキング(海賊)やspam(スパム)については、python言語の名前の由来となったコメディ番組のモンティパイソンに出てくる「spam」から起因したものです。
迷惑をあらわすspamが生まれたのもモンティパイソン由来です。
メニューがスパムだらけの食堂で、海賊がスパムの歌を歌うシーンがあるのですが・・・
これは「モンティパイソン スパム 動画」で確認してみてください。
#Variable assignment 変数への割り当て
spam_amount = 0
spam_amountという変数を作り、イコール演算子を使って0を割り当てます。
javaやC++を使っている人は、変数への割り当てについて以下の様な点に気づくかもしれません。
・変数への割り当ての時に「宣言(declare)」する必要はありません
・割り当てる値の属性(type)を明示する必要がありません。数字で入れたものを後から文字列やブール値にする事もできます。
#Function calls 関数の呼び出し
print(spam_amount)
0
呼び出し関数
printは値を画面に表示する関数です。
括弧の中に値を入力する、もしくは変数を入力します。
# Ordering Spam, egg, Spam, Spam, bacon and Spam (4 more servings of Spam)
spam_amount = spam_amount + 4
最初の一行はコメントをしています。pythonではシャープ記号を使ってコード中にコメントすることができます。
次に再割り当てを行っています。またイコール演算子を使って代入しています。
pythonの内部ではspam_amountの中に入っていた値0に4を足す操作が行われ、それを左の変数に割り当てます。
if spam_amount > 0:
print("But I don't want ANY spam!")
viking_song = "Spam Spam Spam"
print(viking_song)
But I don't want ANY spam!
Spam Spam Spam
条件に付いてはあまり詳細には語りませんが、何をしているのかはコードを見ると推測できるのでは無いでしょうか?
pythonはこういった読みやすくシンプルな点が評価されている言語なのです。
spamが正の値である条件を満たした時にのみ"もうspamなんていらない!"という表示が実行されますが、下のviking_songは条件がついていないため必ず実行されます。
python内部ではこの二つのprintはどうやって区別されているのでしょうか?
if文の末尾に注目するとコロン記号が使われていて、コロンよりも下が「コードブロック(code block)」として認識されます。
pythonのコードブロックはインデントされた範囲が処理対象となります。
他の言語では中括弧で囲われた部分がif文の処理対象になっていることが多いです。
spaceが意味を持つという点は他言語ユーザーに取っては驚きかもしれませんが、インデントを強制することでpythonは他言語よりも高い可読性を獲得しているのです。
viking_songの変数割り当てからインデントがされていないためif文の条件から外れるのです。
文字列についても説明しよう。
"But I don't want ANY spam!"
コードの一部分(code snippet)を確認すると、ダブルもしくはシングルクオーテーションが使われているコードに遭遇するだろう。
文字列中にシングルクオーテーションが存在している場合はダブルクォーテーションで囲ってあげる。
シングルクオーテーションが文中で使用されている場合、シングルクオーテーションで囲ってしまうとpythonがどこからどこまでが文字列として処理するべきなのか混乱してしまう。
viking_song = "Spam " * spam_amount
print(viking_song)
Spam Spam Spam Spam
アスタリスク記号を使う事で数値の掛け算ができます。
3 * 3 は 9になるでしょう。
面白いことに文字列を乗算すると繰り返しができるのです。
プラス記号で文字列同士の足し算などもできます。
#数値の演算
spam_amount = 0
既に数値を引数に渡す方法は紹介しました。
python内部ではどのようなデータの属性として認識されているのか?
pythonに聞いてみましょう。
type(spam_amount)
int
intとは整数(integer)のことです。
pythonの数値で他の属性に出会うこともあるでしょう。
type(19.95)
float
浮動小数は小数点以下の数値を含む値です。
重みや比率などを表す時に使うでしょう。
type関数はprint関数と同じく組み込まれている関数で、「どんな種類の物ですか?」とpythonに尋ねる時に使います。
他にも算術で使う演算子を紹介します。
a + b 足し算
a - b 引き算
a * b 掛け算
a / b 割り算
a // b 割り算を行い小数部分を除去(床関数)
a % b 割り算のあまり
a ** b aのb乗
-a 負の値にする
電卓の割り算記号はpythonではスラッシュです。
print(5 / 2)
print(6 / 2)
2.5
3.0
スラッシュ二つを使うと
print(5 // 2)
print(6 // 2)
2
3
少数が切り捨てられました。
#計算の順序
「掛け算や割り算が足し算や引き算よりも優先される」という内容は小学生の時に学ぶと思います。
8 - 3 + 2 = 7
-3 + 4 * 2 = 5
この順序を無視して記述すると
hat_height_cm = 25
my_height_cm = 190
# How tall am I, in meters, when wearing my hat?
total_height_meters = hat_height_cm + my_height_cm / 100
print("Height in meters =", total_height_meters, "?")
Height in meters = 26.9 ?
予期せぬ結果が返ってきます。
なので括弧を使って優先順位を明示しましょう。
total_height_meters = (hat_height_cm + my_height_cm) / 100
print("Height in meters =", total_height_meters)
Height in meters = 2.15
#数値に関する組み込み関数
maxやminといった関数を使ってみると、最大値、最小値を返します。
print(min(1, 2, 3))
print(max(1, 2, 3))
1
3
absは絶対値を表示します。
print(abs(32))
print(abs(-32))
32
32
データの属性を指定してやることもできます。
print(float(10))
print(int(3.33))
# They can even be called on strings!
print(int('807') + 1)
10.0
3
808
#一回目はここまで。実際に打ち込んでみてくださいね。
#1-2.fanctions and getting help
absやprintは確認しましたがまだまだ関数が多くあります。
独自の関数を定義していくことがpythonプログラミングにとっては多く出てくる事でしょう。
定義の方法と呼び出し方について見ていきましょう。
#ヘルプを表示
abs関数が何をするのか忘れた時はどうしましょう。
help関数は様々な時にあなたを助けてくれるでしょう。
help(round)
Help on built-in function round in module builtins:
round(...)
round(number[, ndigits]) -> number
Round a number to a given precision in decimal digits (default 0 digits).
This returns an int when called with one argument, otherwise the
same type as the number. ndigits may be negative.
help関数を使うと、
・関数のヘッダーを確認できる。(数値を扱えることなどが分かる)
・関数の簡単な説明が確認できる。
help関数は関数の結果を渡すのではなく、関数そのものの名前を渡してください。
help(print)
Help on built-in function print in module builtins:
print(...)
print(value, ..., sep=' ', end='\n', file=sys.stdout, flush=False)
Prints the values to a stream, or to sys.stdout by default.
Optional keyword arguments:
file: a file-like object (stream); defaults to the current sys.stdout.
sep: string inserted between values, default a space.
end: string appended after the last value, default a newline.
flush: whether to forcibly flush the stream.
print関数の中にはsepという引数があることや、文末記号がバックスラッシュnであると分かりました。
関数についている機能をhelpで確認して関数をもっと高度に使いこなしましょう。
#関数の定義
組み込み関数だけでなくあなた自身で関数を作ってみましょう。
def least_difference(a, b, c):
diff1 = abs(a - b)
diff2 = abs(b - c)
diff3 = abs(a - c)
return min(diff1, diff2, diff3)
a,b,cという三つの引数を持つleast_difference関数を作ることが出来ました。
関数の定義はdefというキーワードをまず置き、関数の中身をコロン記号以下のインデントしたコードブロックで作ります。
returnを検知すると処理を終了し、値を返します。
今回の例では三つの引数に渡された数値同士を引き算して最も値が小さくなった時の値を出力します。
print(
least_difference(1, 10, 100),
least_difference(1, 10, 10),
least_difference(5, 6, 7), # Python allows trailing commas in argument lists. How nice is that?
)
9 0 1
忘れたらhelp関数を使いましょう。
help(least_difference)
Help on function least_difference in module __main__:
least_difference(a, b, c)
自分で定義した関数の説明が無く、あまり親切では無いので説明文をつけてやりましょう。
#説明文をつける(Docstrings)
def least_difference(a, b, c):
"""Return the smallest difference between any two numbers
among a, b and c.
>>> least_difference(1, 5, -5)
4
"""
diff1 = abs(a - b)
diff2 = abs(b - c)
diff3 = abs(a - c)
return min(diff1, diff2, diff3)
三重括弧(クオーテーション)で囲まれた部分が説明文として表示させることのできる部分です。
helpで確認してみましょう。
help(least_difference)
Help on function least_difference in module __main__:
least_difference(a, b, c)
Return the smallest difference between any two numbers
among a, b and c.
>>> least_difference(1, 5, -5)
4
helpから自分の記入したドキュメントが確認できた。
優れたプログラマーは直ぐに破棄するような関数出ない限りdocstringsを記入します。
使用する誰かの助けにもなりますので実行例なども入れておきましょう。
#関数が値を返さない時
returnが無い時はどうなるでしょうか?
def least_difference(a, b, c):
"""Return the smallest difference between any two numbers
among a, b and c.
"""
diff1 = abs(a - b)
diff2 = abs(b - c)
diff3 = abs(a - c)
min(diff1, diff2, diff3)
print(
least_difference(1, 10, 100),
least_difference(1, 10, 10),
least_difference(5, 6, 7),
)
None None None
Noneは他言語でのnullに近い値です。
returnが無ければ値を返すことが無いのです。
mystery = print()
print(mystery)
None
#オプションの引数
help(print)を実行したときに、printにはオプションの引数sepがあることが分かりました。
使ってみましょう。
print(1, 2, 3, sep=' < ')
1 < 2 < 3
小なり記号で区切られました。
デフォルトではスペースが1つ入力されています。
print(1, 2, 3)
1 2 3
関数の中にデフォルトの処理を入れておけば面倒な処理は楽になります。
def greet(who="Colin"):
print("Hello,", who)
greet()
greet(who="Kaggle")
# (In this case, we don't need to specify the name of the argument, because it's unambiguous.)
greet("world")
Hello, Colin
Hello, Kaggle
Hello, world
helloを毎回入力しなくてすみました。
#関数に関数を使う
関数自体を関数の引数として渡すことができます。
例を見てみましょう。
def mult_by_five(x):
return 5 * x
def call(fn, arg):
"""Call fn on arg"""
return fn(arg)
def squared_call(fn, arg):
"""Call fn on the result of calling fn on arg"""
return fn(fn(arg))
print(
call(mult_by_five, 1),
squared_call(mult_by_five, 1),
sep='\n', # '\n' is the newline character - it starts a new line
)
5
25
5を掛け算するmult_by_five関数を作り、mult_by_five関数に数値を入力させるcall関数を作り、さらにcallを二乗させる関数を作っている。
関数を使う関数を「高級関数(Higher order functions)」と呼びます。
もう一つ例を見てみましょう。
max関数とは最大値を返すものですが、keyとして関数を指定してやると、関数の条件を満たす数値の中での最大値を返すことができます。
def mod_5(x):
"""Return the remainder of x after dividing by 5"""
return x % 5
print(
'Which number is biggest?',
max(100, 51, 14),
'Which number is the biggest modulo 5?',
max(100, 51, 14, key=mod_5),
sep='\n',
)
Which number is biggest?
100
Which number is the biggest modulo 5?
14
#二回目はここまで。
#1-3.booleans and conditionals
pythonでbool型というとTRUE,FALSEという二種類の値を取ります。
x = True
print(x)
print(type(x))
True
<class 'bool'>
直接TRUE,FALSEを入力するのではなく、基本的に処理結果や条件のyes,noを判断するときに使われます。
処理で使用する演算子をいかに例示します。
#比較演算子
a == b aとbが等しいかどうか
a != b aとbが等しくないかどうか
a < b aがbよりも小さいか
a > b aがbよりも大きいか
a <= b aがbと等しいもしくはbよりも小さいかどうか
a >= b aがbと等しいもしくはbよりも大きいかどうか
def can_run_for_president(age):
"""Can someone of the given age run for president in the US?"""
# The US Constitution says you must "have attained to the Age of thirty-five Years"
return age >= 35
print("Can a 19-year-old run for president?", can_run_for_president(19))
print("Can a 45-year-old run for president?", can_run_for_president(45))
Can a 19-year-old run for president? False
Can a 45-year-old run for president? True
19歳は大統領に立候補できないですよね。
3.0 == 3
True
floatもintも同じ数字なので、データの属性が異なっても等しいと判断してくれます。
'3' == 3
False
さすがに文字と同じとは認識はしてくれません。
比較演算子を使えば奇数かどうかの判断をどれだけ大きな数字に対しても行ってくれます。
def is_odd(n):
return (n % 2) == 1
print("Is 100 odd?", is_odd(100))
print("Is -1 odd?", is_odd(-1))
Is 100 odd? False
Is -1 odd? True
比較演算子は==です。=として定義すると代入されてしまうので注意。
#ブール型の組み合わせ
条件をすべて満たす場合をTRUEとしたい場合は、組み合わせる事もできます。
たとえば大統領になれる条件として年齢だけでなく、国籍を追加したいときは以下のように組み合わせます。
def can_run_for_president(age, is_natural_born_citizen):
"""Can someone of the given age and citizenship status run for president in the US?"""
# The US Constitution says you must be a natural born citizen *and* at least 35 years old
return is_natural_born_citizen and (age >= 35)
print(can_run_for_president(19, True))
print(can_run_for_president(55, False))
print(can_run_for_president(55, True))
False
False
True
クイズですが、こんな場合はどうなるでしょうか?
True or True and False
正解はTRUEです。
pythonの条件には優先順位ルールがあります。
たとえばandの優先順位はorよりも高いので、最も優先順位の高い処理の結果はTrueです。
条件の優先順位を暗記するよりも確実なことは、括弧でくくることです。
括弧を使って括弧内を強制的に優先させることで、コードの可読性やバグの防止に繋がります。
たとえば、次のような式があったら
prepared_for_weather = have_umbrella or rain_level < 5 and have_hood or not rain_level > 0 and is_workday
傘を持っている もしくは 雨が弱くてフード付きの服を来ている もしくは 雨だとしても仕事の日でない
こんな文があったとしてもコードとして読みにくい上にバグの原因ですので修正しましょう。
prepared_for_weather = have_umbrella or (rain_level < 5 and have_hood) or not (rain_level > 0 and is_workday)
読みやすくするためならもっと括弧を追加してもいいです。
prepared_for_weather = have_umbrella or ((rain_level < 5) and have_hood) or (not (rain_level > 0 and is_workday))
可読性をあげるためにインデントを使ってブロック化してあげることもできます。
prepared_for_weather = (
have_umbrella
or ((rain_level < 5) and have_hood)
or (not (rain_level > 0 and is_workday))
)
#条件文
bool型の値とはif, elif, elseを組み合わせて本当に使えるようになります。
条件文とboolを使って特定のコードのみを実行させてみましょう。
def inspect(x):
if x == 0:
print(x, "is zero")
elif x > 0:
print(x, "is positive")
elif x < 0:
print(x, "is negative")
else:
print(x, "is unlike anything I've ever seen...")
inspect(0)
inspect(-15)
0 is zero
-15 is negative
python独特の条件文としてelifがありますが、他の言語でのelse ifの短縮形です。
elifやelseがそれぞれコロン記号を持っていることに注意してください。
条件文の後のコードはインデントを持ったコードブロックとして認識されます。
構造に注意しましょう。
def f(x):
if x > 0:
print("Only printed when x is positive; x =", x)
print("Also only printed when x is positive; x =", x)
print("Always printed, regardless of x's value; x =", x)
f(1)
f(0)
Only printed when x is positive; x = 1
Also only printed when x is positive; x = 1
Always printed, regardless of x's value; x = 1
Always printed, regardless of x's value; x = 0
#bool型に変換
入力の属性を変更する関数はint()やfloat()だけでなくbool()もあるのです。
print(bool(1)) # all numbers are treated as true, except 0
print(bool(0))
print(bool("asf")) # all strings are treated as true, except the empty string ""
print(bool(""))
# Generally empty sequences (strings, lists, and other types we've yet to see like lists and tuples)
# are "falsey" and the rest are "truthy"
True
False
True
False
bool型でなくとも、黙認的にboolとして処理する場合があります。
if 0:
print(0)
elif "spam":
print("spam")
spam
#条件式の表現
条件によって二つの出力のうちどちらかを選ばせることは一般的でしょう。
def quiz_message(grade):
if grade < 50:
outcome = 'failed'
else:
outcome = 'passed'
print('You', outcome, 'the quiz with a grade of', grade)
quiz_message(80)
You passed the quiz with a grade of 80
pythonではもっと簡素にすることができます。
def quiz_message(grade):
outcome = 'failed' if grade < 50 else 'passed'
print('You', outcome, 'the quiz with a grade of', grade)
quiz_message(45)
You failed the quiz with a grade of 45
他の言語では三項演算子と呼ばれる部分です。
javascriptならば
var outcome = grade < 50 ? 'failed' : 'passed
と書きますが、可読性はpythonの勝利でしょう。
#3回目はここまで。
#1-4. lists
リストというものを作ってみます。
primes = [2, 3, 5, 7]
数値以外も入ります。
planets = ['Mercury', 'Venus', 'Earth', 'Mars', 'Jupiter', 'Saturn', 'Uranus', 'Neptune']
リストにリストを入れ子状にしてみます。
hands = [
['J', 'Q', 'K'],
['2', '2', '2'],
['6', 'A', 'K'], # (Comma after the last element is optional)
]
# (I could also have written this on one line, but it can get hard to read)
hands = [['J', 'Q', 'K'], ['2', '2', '2'], ['6', 'A', 'K']]
リストには色んなものが入ります。
my_favourite_things = [32, 'raindrops on roses', help]
# (Yes, Python's help function is *definitely* one of my favourite things)
#索引
角括弧でリストの中の特定要素にアクセスできます。
太陽に最も近い惑星はどこでしょうか?
planets[0]
'Mercury'
pythonのindexは0から始まりますので、一つ目の要素にアクセスするには[0]を指定します。
逆に最も遠い惑星は?
planets[-1]
'Neptune'
逆順にして教えてくれます。
-0ではありません。
#スライス
planets[0:3]
['Mercury', 'Venus', 'Earth']
リストのうち三つを連続表示させたいときは上記のようにコロン記号を使用します。
開始インデックスと終了インデックスを指定します。
開始や終了を省略した場合、自動的に一番最初や一番最後が指定されたと認識します。
planets[:3]
planets[3:]
['Mercury', 'Venus', 'Earth']
['Mars', 'Jupiter', 'Saturn', 'Uranus', 'Neptune']
負のインデックスも指定することができます。
# All the planets except the first and last
planets[1:-1]
['Venus', 'Earth', 'Mars', 'Jupiter', 'Saturn', 'Uranus']
# The last 3 planets
planets[-3:]
['Saturn', 'Uranus', 'Neptune']
#リストの書き換え
リストは可変です。mutableです。
変更方法は、リストの変更したい部分をインデックス指定してやるか、スライスを使います。
planets[3] = 'Malacandra'
planets
['Mercury',
'Venus',
'Earth',
'Malacandra',
'Jupiter',
'Saturn',
'Uranus',
'Neptune']
最初三つを言いやすくしてやりましょう。
planets[:3] = ['Mur', 'Vee', 'Ur']
print(planets)
# That was silly. Let's give them back their old names
planets[:4] = ['Mercury', 'Venus', 'Earth', 'Mars',]
['Mur', 'Vee', 'Ur', 'Malacandra', 'Jupiter', 'Saturn', 'Uranus', 'Neptune']
#リスト関数
リストに関係する関数としてlen()があります。
# How many planets are there?
len(planets)
8
リストの中の要素の数を調べることができます。
ほかにも
# The planets sorted in alphabetical order
sorted(planets)
['Earth', 'Jupiter', 'Mars', 'Mercury', 'Neptune', 'Saturn', 'Uranus', 'Venus']
順番を並べ替えるsorted関数
primes = [2, 3, 5, 7]
sum(primes)
17
合計値を計算するsum関数
max(primes)
7
リストの中の最大値も表示させられます。
#objectについて
objectはドット記号を使ってアクセスします。
例えば、pythonの数値には虚数部分を格納することが出来て、虚数部分にはimagでアクセスできます。
x = 12
# x is a real number, so its imaginary part is 0.
print(x.imag)
# Here's how to make a complex number, in case you've ever been curious:
c = 12 + 3j
print(c.imag)
0
3.0
object部分に関数を収納することもできます。
objectに入れられた関数はmethodと呼ばれます。
(imagなどは属性(attributes)と呼ばれます。)
たとえばメソッドの一つに、変数の中に入っているデータのビット数を調べるbit_length関数があります。
x.bit_length
<function int.bit_length>
変数の中にbit_length関数がくっついていることが分かりました。
使ってみましょう。
x.bit_length()
4
xの中の二文字の数値は4 bitの情報をもっています。
メソッドとして入っている関数はhelp関数で詳細を確認することもできます。
help(x.bit_length)
Help on built-in function bit_length:
bit_length(...) method of builtins.int instance
int.bit_length() -> int
Number of bits necessary to represent self in binary.
>>> bin(37)
'0b100101'
>>> (37).bit_length()
6
メソッドは分かりにくいですが、リストを使っているとメソッドを頻繁に使うことがあります。
#リストメソッド
.appendというメソッドはリストの末尾に要素を追加することができます。
# Pluto is a planet darn it!
planets.append('Pluto')
余談ですが、help(append)でappendという関数を調べようとしても、pythonは関数が無いと怒るでしょう。
なぜならappendはlistに付随するものだからです。
関数として詳細がしりたければ、help(list.append)からアクセスする必要がありあます。
help(planets.append)
Help on built-in function append:
append(...) method of builtins.list instance
L.append(object) -> None -- append object to end
planets
['Mercury',
'Venus',
'Earth',
'Mars',
'Jupiter',
'Saturn',
'Uranus',
'Neptune',
'Pluto']
popというメソッドはリストの最後の要素にアクセスしてから、リストの最後の要素を削除するメソッドです。
planets.pop()
'Pluto'
planets
['Mercury', 'Venus', 'Earth', 'Mars', 'Jupiter', 'Saturn', 'Uranus', 'Neptune']
#リストの中身を検索する
planets.index('Earth')
2
2つめではありません。pythonは0始まりなのでリストの三つ目に地球があることが分かりました。
in演算子を使うとリスト内に存在するか調べられます。
# Is Earth a planet?
"Earth" in planets
True
他にもリストに付随するメソッドはあります。
もし知りたければ、あなたの定義したリストそのものをhelp()に渡してあげましょう。
#タプル
タプルとは感覚的にはリストと同じです。
注意点は
・角括弧[]でなく丸括弧()をつかうこと
t = (1, 2, 3)
・中身を変更することができない
t[0] = 100
TypeError: 'tuple' object does not support item assignment
タプルは複数の返り値を持つ関数に使われています。
x = 0.125
x.as_integer_ratio()
(1, 8)
タプルで且つ複数の返り値を、変数に割り当てたいときは、変数も複数用意しましょう。
numerator, denominator = x.as_integer_ratio()
print(numerator / denominator)
0.125
それぞれの変数に1と8がバラバラに割り当てられました。
#4回目はここまで。
#1-5. Loops and List Comprehensions
ループとは繰り返し同じ処理を行うことです。
planets = ['Mercury', 'Venus', 'Earth', 'Mars', 'Jupiter', 'Saturn', 'Uranus', 'Neptune']
for planet in planets:
print(planet, end=' ') # print all on same line
Mercury Venus Earth Mars Jupiter Saturn Uranus Neptune
上記ループ文では
・元の変数(planets)
と
・格納先の変数(planet)
を必要としています。
もとの変数からひとつづつ取り出し、これを格納先に入力しているのです。
文字だけでなく数字にも使えます。
multiplicands = (2, 2, 2, 3, 3, 5)
product = 1
for mult in multiplicands:
product = product * mult
product
360
文字列の中の要素にも使えます。
s = 'steganograpHy is the practicE of conceaLing a file, message, image, or video within another fiLe, message, image, Or video.'
msg = ''
# print all the uppercase letters in s, one at a time
for char in s:
if char.isupper():
print(char, end='')
HELLO
大文字であるものを取り出して順番に繰り返している。
#range関数
range関数とは、数字を順番に発生させる関数です。
繰り返し文と相性が良く、何回繰り返したいかを入力することができます。
for i in range(5):
print("Doing important work. i =", i)
Doing important work. i = 0
Doing important work. i = 1
Doing important work. i = 2
Doing important work. i = 3
Doing important work. i = 4
#whileを使ったループ文
i = 0
while i < 10:
print(i, end=' ')
i += 1
0 1 2 3 4 5 6 7 8 9
条件がFALSEになるまで繰り返します。
条件が満たされないコードを書いてしまうと永遠に処理し続けるので注意。
#リストの内包関係
squares = [n**2 for n in range(10)]
squares
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
これはいい例。
内包表記を使わずにやろうとすると以下のようになります。
squares = []
for n in range(10):
squares.append(n**2)
squares
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
if文を付け足すこともできます。
short_planets = [planet for planet in planets if len(planet) < 6]
short_planets
['Venus', 'Earth', 'Mars']
SQLに精通している人は、WHERE句と同じであると言ったほうが分かりやすいかもしれません。
# str.upper() returns an all-caps version of a string
loud_short_planets = [planet.upper() + '!' for planet in planets if len(planet) < 6]
loud_short_planets
['VENUS!', 'EARTH!', 'MARS!']
三行にしたほうが分かりやすいかもしれません。
[
planet.upper() + '!'
for planet in planets
if len(planet) < 6
]
['VENUS!', 'EARTH!', 'MARS!']
SQLを使っている人ならば、select,from,whereが三行に当てはまることが分かるかもしれません。
クイズです。
[32 for planet in planets]
はどうなるでしょう?
答えは
[32, 32, 32, 32, 32, 32, 32, 32]
リスト内包表記を使うと複数行かかっていたコードが一行になることもあります。
たとえば
def count_negatives(nums):
"""Return the number of negative numbers in the given list.
>>> count_negatives([5, -1, -2, 0, 3])
2
"""
n_negative = 0
for num in nums:
if num < 0:
n_negative = n_negative + 1
return n_negative
という複数行が
def count_negatives(nums):
return len([num for num in nums if num < 0])
たったこれだけ。
リマインドになりますが、ブール型の足し算とは、Trueの回数を合計してくれるので、
True + True + False + Trueは3となります。
def count_negatives(nums):
# Reminder: in the "booleans and conditionals" exercises, we learned about a quirk of
# Python where it calculates something like True + True + False + True to be equal to 3.
return sum([num < 0 for num in nums])
少ない行で書くことが良いコードであるとは限りません。
The Zen of Pythonにはこう書かれています。
Readability counts.
Explicit is better than implicit.
読みやすさが重要です。と。
以上を元に、読みやすくコンパクトなコードを書きましょう。
読みやすさとコンパクト性で迷ったら読みやすいコードを意識して書きましょう。
#5回目はここまで。
#1-6. strings and dictionaries
#文字型
pythonのデータ分析で文字列操作は大変しやすいです。
pythonではシングルもしくはダブルクオーテーションで囲まれている物を文字列として認識するのでした。
シングルでもダブルでも等価の文字列として扱われます。
x = 'Pluto is a planet'
y = "Pluto is a planet"
x == y
True
文字列にシングルクオーテーションが使われている時はダブルクオーテーションで囲みましょう。
たとえばアポストロフィー記号としてシングルクオーテーションが使われている場合があります。
print("Pluto's a planet!")
print('My dog is named "Pluto"')
Pluto's a planet!
My dog is named "Pluto"
シングルクオーテーションの文をシングルクオーテーションで囲むとpythonは混乱してしまいます。
'Pluto's a planet!'
File "<ipython-input-3-a43631749f52>", line 1
'Pluto's a planet!'
^
SyntaxError: invalid syntax
どうしてもシングルクオーテーションで囲みたい時は、文中のアポストロフィーをバックスラッシュでエスケープさせましょう。
'Pluto\'s a planet!'
"Pluto's a planet!"
他にも文字の特別な表示方法についてまとめておきます。
入力時の入力 -> python出力
'What's up?' -> What's up?
"That's "cool"" -> That's "cool"
"Look, a mountain: /\" -> Look, a mountain: /\
"1\n2 3" ->
1
2 3
(改行される)
また、三重クオーテーションで囲むと、改行が記号を用いずとも適用される。
これはDocstringでも語った。
triplequoted_hello = """hello
world"""
print(triplequoted_hello)
triplequoted_hello == hello
hello
world
True
print関数に文字を渡すと一行ずつ表示されます。これは文末に改行記号がデフォルトで挿入されているためです。
文末の記号を指定しなかった場合改行されません。
print("hello")
print("world")
print("hello", end='')
print("pluto", end='')
hello
world
hellopluto
#文字の順番
文字は順番として考えることができる。
インデックスを指定して取り出したり
# Indexing
planet = 'Pluto'
planet[0]
'P'
切り取ったり
# Slicing
planet[-3:]
'uto'
長さをカウントしたりできる。
# How long is this string?
len(planet)
5
長さを持っているならリストと同じくループで一文字ずつ処理することもできる。
# Yes, we can even loop over them
[char+'! ' for char in planet]
['P! ', 'l! ', 'u! ', 't! ', 'o! ']
ただし部分的に書き換えることはできない。
タプルに似ている。
planet[0] = 'B'
# planet.append doesn't work either
TypeError: 'str' object does not support item assignment
#文字列に関するメソッド
リストと同じく、文字列には色々なメソッドがついています。
大文字化
# ALL CAPS
claim = "Pluto is a planet!"
claim.upper()
'PLUTO IS A PLANET!'
小文字化
# all lowercase
claim.lower()
'pluto is a planet!'
何文字目から始まるか。
切り出すときに便利。
# Searching for the first index of a substring
claim.index('plan')
11
開始文字を指定。
複数行あるときに特定の言葉から始まる行を取り出すのに便利。
claim.startswith(planet)
True
終わり文字を指定
claim.endswith('dwarf planet')
False
#文字をリストに変換
文字列はそれだけでタプルのような扱いができていたが、明示的にリストにするメソッドを使ってみよう。
words = claim.split()
words
['Pluto', 'is', 'a', 'planet!']
スペースを認識してリスト型にしてくれた。
単語帳のようになっている。
区切り文字を指定することで年月日などが分けやすくなる。
datestr = '1956-01-31'
year, month, day = datestr.split('-')
分割された文字は三つの変数に分かれて代入された。
joinメソッドを使えば特定文字で結合できる。
'/'.join([month, day, year])
'01/31/1956'
unicodeだって扱えちゃいます。
# Yes, we can put unicode characters right in our string literals :)
' 👏 '.join([word.upper() for word in words])
'PLUTO 👏 IS 👏 A 👏 PLANET!'
#formatメソッドを使う
pythonでは文字列の足し算が行える。
planet + ', we miss you.'
'Pluto, we miss you.'
文字列以外を加えたい時は、明示的にstr関数を使って文字列に変換しておきましょう。
position = 9
planet + ", you'll always be the " + position + "th planet to me."
TypeError: must be str, not int
planet + ", you'll always be the " + str(position) + "th planet to me."
"Pluto, you'll always be the 9th planet to me."
ですが、これでは可読性が下がります。そんなときはformatを使ってみましょう。
"{}, you'll always be the {}th planet to me.".format(planet, position)
"Pluto, you'll always be the 9th planet to me."
読みやすくなりました!
format()内に入力した単語を、中括弧の中に順番に入れてくれる関数です。
文字列の中にintがそのまま入りました。
formatがintをstrに自動で処理してくれていたからです。
とても便利です。他にも数字の位の表示指定や率に変換したりコンマを追加するなどが可能です。
pluto_mass = 1.303 * 10**22
earth_mass = 5.9722 * 10**24
population = 52910390
# 2 decimal points 3 decimal points, format as percent separate with commas
"{} weighs about {:.2} kilograms ({:.3%} of Earth's mass). It is home to {:,} Plutonians.".format(
planet, pluto_mass, pluto_mass / earth_mass, population,
)
"Pluto weighs about 1.3e+22 kilograms (0.218% of Earth's mass). It is home to 52,910,390 Plutonians."
# Referring to format() arguments by index, starting from 0
s = """Pluto's a {0}.
No, it's a {1}.
{0}!
{1}!""".format('planet', 'dwarf planet')
print(s)
Pluto's a planet.
No, it's a dwarf planet.
planet!
dwarf planet!
str.formatをもっと知りたければ、pythonの公式ドキュメントで調べてください。
#辞書型
pythonでは辞書型という数字と文字を対応させて持ったデータを作ることができます。
numbers = {'one':1, 'two':2, 'three':3}
この場合、文字がkeyで数字がvalueと呼ばれます。
辞書型の場合は角括弧でアクセスします。
numbers['one']
1
角括弧を使うと新たなkeyを追加することができます。
numbers['eleven'] = 11
numbers
{'one': 1, 'two': 2, 'three': 3, 'eleven': 11}
既にあるあ値を書き換えることもできます。
numbers['one'] = 'Pluto'
numbers
{'one': 'Pluto', 'two': 2, 'three': 3, 'eleven': 11}
pythonの辞書型には、リスト内包表記と同じく、辞書内包表記が使えます。
planets = ['Mercury', 'Venus', 'Earth', 'Mars', 'Jupiter', 'Saturn', 'Uranus', 'Neptune']
planet_to_initial = {planet: planet[0] for planet in planets}
planet_to_initial
{'Mercury': 'M',
'Venus': 'V',
'Earth': 'E',
'Mars': 'M',
'Jupiter': 'J',
'Saturn': 'S',
'Uranus': 'U',
'Neptune': 'N'}
in演算子を使うと、keyに対象が存在するかを検索できます。
'Saturn' in planet_to_initial
True
'Betelgeuse' in planet_to_initial
False
辞書のfor文は以下のようにkeyを順番に取ってきます。
for k in numbers:
print("{} = {}".format(k, numbers[k]))
one = Pluto
two = 2
three = 3
eleven = 11
.keys()や.values()のメソッドを使うと、それぞれの値を取得できます。
試しにvalueをvaluesメソッドを使って取って来ましょう。
アルファベット順に並べて、スペースで形を整えて表示させます。
# Get all the initials, sort them alphabetically, and put them in a space-separated string.
' '.join(sorted(planet_to_initial.values()))
'E J M M N S U V'
.items()メソッドを使うと、keyとvalueが同時に取得できます。
それぞれをfor文で同時に1つずつ取得して、二つの変数に代入し、printしてみましょう。
for planet, initial in planet_to_initial.items():
print("{} begins with \"{}\"".format(planet.rjust(10), initial))
Mercury begins with "M"
Venus begins with "V"
Earth begins with "E"
Mars begins with "M"
Jupiter begins with "J"
Saturn begins with "S"
Uranus begins with "U"
Neptune begins with "N"
もっと辞書型についてしりたければhelp関数で中身を見てみましょう。
help(dict)
#六回はここまで
#1-7. working with external libraries
#importについて
今回はライブラリの読み込みについて説明する。
今まで組み込まれている関数についての説明を行ってきた。
しかし、pythonの便利な所は、豊富で高品質のライブラリがあること。
データサイエンティストにとってとても有用に感じる。
これらのライブラリの一部は標準ライブラリとして存在しています。
他のライブラリの場合は追加するという一手順が必要になりますが、そんなに難しいことではありません。
mathというライブラリをimportしてみましょう。
import math
print("It's math! It has type {}".format(type(math)))
It's math! It has type <class 'module'>
mathはmoduleです。
moduleとは誰かが定義した変数の集まりの事です。
dir()関数を使って中身を確認することができます。
print(dir(math))
['__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'acos', 'acosh', 'asin', 'asinh', 'atan', 'atan2', 'atanh', 'ceil', 'copysign', 'cos', 'cosh', 'degrees', 'e', 'erf', 'erfc', 'exp', 'expm1', 'fabs', 'factorial', 'floor', 'fmod', 'frexp', 'fsum', 'gamma', 'gcd', 'hypot', 'inf', 'isclose', 'isfinite', 'isinf', 'isnan', 'ldexp', 'lgamma', 'log', 'log10', 'log1p', 'log2', 'modf', 'nan', 'pi', 'pow', 'radians', 'sin', 'sinh', 'sqrt', 'tan', 'tanh', 'tau', 'trunc']
これらの変数にアクセスするには、ドット記号を付てアクセスできます。例えば円周率についてアクセスしてみましょう。
print("pi to 4 significant digits = {:.4}".format(math.pi))
pi to 4 significant digits = 3.142
しかし、logなどは関数になっています。
なにが引数になっているのでしょうか。
math.log(32, 2)
5.0
そんな時はhelpを使うのでしたね。
help(math.log)
Help on built-in function log in module math:
log(...)
log(x[, base])
Return the logarithm of x to the given base.
If the base not specified, returns the natural logarithm (base e) of x.
他の関数や変数についても知りたい場合はライブラリ自体をhelpで見てみましょう。
help(math)
#その他import方法
import math as mt
mt.pi
3.141592653589793
上記のようにimportしたライブラリの名前を短くすることができます。
mathはそれ自体とても短いですが、numpy,pandas,matplotlibなどは、np,pd,pltとして短く略されることが一般的です。
import文に出てきたasのやっていることは内部ではこうなっています。
import math
mt = math
毎回円周率piを呼び出す時にライブラリ名を明記しなければならないのでしょうか?
実は省略もできるのです。
from math import *
print(pi, log(32, 2))
3.141592653589793 5.0
アスタリスクを使ってimportすると、ドット記号を使ってライブラリにアクセスせずとも中身が使えるのです。
ただ、便利な反面、コードを正しく使いたい人からこの使い方を批判される時もあります。
彼らの意見も最もなのです。たとえばこんな弊害があります。
from math import *
from numpy import *
print(pi, log(32, 2))
TypeError: return arrays must be of ArrayType
アスタリスクでimportする方法(star import)では、後々デバックが困難になる場合があります。
このエラーはmathとnumpyのモジュールを使いたかったのですが、mathがnumpyに上書き(overwriteもしくはshadows)されてしまっており、
log関数のセマンティクスが異なる状態になってしまっていたのです。
改善方法としては、必要な関数や変数を指定して読み込む事です。
from math import log, pi
from numpy import asarray
#サブmodules
モジュールは変数や関数という話でしたが、中には他のモジュールを参照するような変数も存在しているのです。
import numpy
print("numpy.random is a", type(numpy.random))
print("it contains names such as...",
dir(numpy.random)[-15:]
)
numpy.random is a <class 'module'>
it contains names such as... ['set_state', 'shuffle', 'standard_cauchy', 'standard_exponential', 'standard_gamma', 'standard_normal', 'standard_t', 'test', 'triangular', 'uniform', 'vonmises', 'wald', 'warnings', 'weibull', 'zipf']
上記のようなnumpyのrandomはモジュールを呼び出すモジュールなので、ドットを二つ使ってrandomの中のモジュールを呼び出さなければなりません。
# Roll 10 dice
rolls = numpy.random.randint(low=1, high=6, size=10)
rolls
array([4, 3, 2, 1, 1, 4, 1, 4, 1, 5])
#ライブラリ独特のデータのタイプ(型)
int,float,bool,list,string,dictを既にマスターしました。(しましたよね?)
これらをマスターしても終わりではありません。
ライブラリを使っていると分かりますが、特殊なタスクで使うライブラリが独自の型を定義している場面を見ると思います。
たとえばmatplotlibの場合、subplots,figures,tickmarks,annotationsなどの型を。
pandasではdataframe,seriesなど。
これらの奇妙なデータ型に出会ったときのサバイバルガイドを示していきます。
#奇妙なデータ型に出会ったら
numpyを使っているとarrayに出会うでしょう。
listでもdictでもない。
あわてないでください。3つの基本的な関数があなたを助けてくれます。
1 - typeを使う
そもそもどんなデータ型なのか教えてくれる。
type(rolls)
numpy.ndarray
2 - dirを使う
何を使ったらいいのかが分かる。
print(dir(rolls))
['T', '__abs__', '__add__', '__and__', '__array__', '__array_finalize__', '__array_function__', '__array_interface__', '__array_prepare__', '__array_priority__', '__array_struct__', '__array_ufunc__', '__array_wrap__', '__bool__', '__class__', '__complex__', '__contains__', '__copy__', '__deepcopy__', '__delattr__', '__delitem__', '__dir__', '__divmod__', '__doc__', '__eq__', '__float__', '__floordiv__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__iand__', '__ifloordiv__', '__ilshift__', '__imatmul__', '__imod__', '__imul__', '__index__', '__init__', '__init_subclass__', '__int__', '__invert__', '__ior__', '__ipow__', '__irshift__', '__isub__', '__iter__', '__itruediv__', '__ixor__', '__le__', '__len__', '__lshift__', '__lt__', '__matmul__', '__mod__', '__mul__', '__ne__', '__neg__', '__new__', '__or__', '__pos__', '__pow__', '__radd__', '__rand__', '__rdivmod__', '__reduce__', '__reduce_ex__', '__repr__', '__rfloordiv__', '__rlshift__', '__rmatmul__', '__rmod__', '__rmul__', '__ror__', '__rpow__', '__rrshift__', '__rshift__', '__rsub__', '__rtruediv__', '__rxor__', '__setattr__', '__setitem__', '__setstate__', '__sizeof__', '__str__', '__sub__', '__subclasshook__', '__truediv__', '__xor__', 'all', 'any', 'argmax', 'argmin', 'argpartition', 'argsort', 'astype', 'base', 'byteswap', 'choose', 'clip', 'compress', 'conj', 'conjugate', 'copy', 'ctypes', 'cumprod', 'cumsum', 'data', 'diagonal', 'dot', 'dtype', 'dump', 'dumps', 'fill', 'flags', 'flat', 'flatten', 'getfield', 'imag', 'item', 'itemset', 'itemsize', 'max', 'mean', 'min', 'nbytes', 'ndim', 'newbyteorder', 'nonzero', 'partition', 'prod', 'ptp', 'put', 'ravel', 'real', 'repeat', 'reshape', 'resize', 'round', 'searchsorted', 'setfield', 'setflags', 'shape', 'size', 'sort', 'squeeze', 'std', 'strides', 'sum', 'swapaxes', 'take', 'tobytes', 'tofile', 'tolist', 'tostring', 'trace', 'transpose', 'var', 'view']
meanが対応していることが分かったので、meanを使ってみると数値が得られた。
# What am I trying to do with this dice roll data? Maybe I want the average roll, in which case the "mean"
# method looks promising...
rolls.mean()
2.6
どうやらtolistも対応しているようなので、arrayを自分の扱いやすいlistに変換しよう。
# Or maybe I just want to get back on familiar ground, in which case I might want to check out "tolist"
rolls.tolist()
[4, 3, 2, 1, 1, 4, 1, 4, 1, 5]
3 - helpを使う
# That "ravel" attribute sounds interesting. I'm a big classical music fan.
help(rolls.ravel)
ラヴェル(ラベル)って響きが面白いですよね。
クラシック作曲者のラヴェルを連想させてくれます。
Help on built-in function ravel:
ravel(...) method of numpy.ndarray instance
a.ravel([order])
Return a flattened array.
Refer to `numpy.ravel` for full documentation.
See Also
--------
numpy.ravel : equivalent function
ndarray.flat : a flat iterator on the array.
もっと知りたいならラベルだけでなく全体をhelpで確認できます。
help(rolls)
``
#演算子について
```py
[3, 4, 1, 2, 2, 1] + 10
TypeError: can only concatenate list (not "int") to list
リストに数値は足し算できないと。
もちろんエラーが返ってきます。
でも・・・
rolls + 10
array([14, 13, 12, 11, 11, 14, 11, 14, 11, 15])
通りました。
listの設計者はリスト内に数値を足し算することができないように規制していましたが、arrayの設計者は数値を計算できるように設計していました。
pythonの中のlistとarrayの違いについて少し下で確認しましょう。
rolls <= 3
array([False, True, True, True, True, False, True, False, True,
False])
xlist = [[1,2,3],[2,4,6],]
# Create a 2-dimensional array
x = numpy.asarray(xlist)
print("xlist = {}\nx =\n{}".format(xlist, x))
xlist = [[1, 2, 3], [2, 4, 6]]
x =
[[1 2 3]
[2 4 6]]
# Get the last element of the second row of our numpy array
x[1,-1]
6
# Get the last element of the second sublist of our nested list?
xlist[1,-1]
TypeError: list indices must be integers or slices, not tuple
numpyのndarrayは多次元のデータ操作のためにインデックスについて独特の定義をしており、各次元ごとにインデックスを指定できるようにしているのです。
#1+1が2にならない
import tensorflow as tf
# Create two constants, each with value 1
a = tf.constant(1)
b = tf.constant(1)
# Add them together to get...
a + b
<tf.Tensor 'add:0' shape=() dtype=int32>
tensorflowという機械学習・深層学習のライブラリを使った足し算をしたときに、a+bは2になりませんでした。
tensorflowのドキュメントを参照してみると、
a symbolic handle to one of the outputs of an Operation.
It does not hold the values of that operation's output, but instead provides a means of computing those values in a TensorFlow tf.Session.
と書いてあります。
ここで重要なのは、int,strings,listなどの演算子について学んできましたが、他のライブラリの関数を使った変数の場合には演算子の意味自体が変わってしまうことがある。
という理解が大切です。たとえばpandasを使って使ったdataframe型の変数について面白い例を見てみましょう。
# Get the rows with population over 1m in South America
df[(df['population'] > 10**6) & (df['continent'] == 'South America')]
こんな書き方をしても機能するのです。
なぜこんな奇妙な書き方でも機能するのか?
謎を知りたいと思った時にはhelpとdirが役にたちます。
print(dir(list))
['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']
そもそも演算子の意味を書き換えていることが分かりました。
2つのアンダースコアで定義されている部分__ lt 、 setattr 、 contains__などは、特別な名前のメソッドを定義していると考えて下さい。
このような2つのアンダースコアに続いて定義される名前はpythonにとって特別な働きをします。
たとえば
x in [1, 2, 3]
は__contains__のリストメソッドを呼び出しています。これは
[1, 2, 3].__contains__(x)
を行っているに等しいです。
もっと知りたければ公式ドキュメントを確認してください。
奇妙なデータ型(type)について見てきました。
あなた自身が独自のtypeを作るレッスンは時間がないので行いませんが、いつか自分自身で定義してその面白さを味わってみてください。
#7回はここまで
現在の更新はここまで。
いかがでしたか?
ColinMorrisさんとてもうまくまとめてくれていて分かりやすかったのではないでしょうか?
さぁ、ここまでやったら早速kaggleに挑戦しましょう!