1. アメダス気象データ分析チャレンジ!(Python版) 1日目 基本編

Copyright 2021 気象ビジネス推進コンソーシアム、岐阜大学 吉野純
(C) 2021 WXBC、岐阜大学 吉野純

<利用条件>
本書は、本書に記載した要件・技術・⽅式に関する内容が変更されないこと、および出典 を明示いただくことを前提に、無償でその全部または⼀部を複製、翻案、翻訳、転記、引 用、公衆送信等して利用できます。なお、全体を複製、翻案、翻訳された場合は、本書にあ る著作権表⽰および利用条件を明⽰してください。

<免責事項>
本書の著作権者は、本書の記載内容に関して、その正確性、商品性、利用目的への適合性 等に関して保証するものではなく、特許権、著作権、その他の権利を侵害していないことを 保証するものでもありません。本書の利用により生じた損害について、本書の著作権者は、 法律上のいかなる責任も負いません。

アメダス気象データ分析チャレンジ!(Python版)にご参加いただきありがとうございます.本日は,まず,Jupyter Notebookを使って,pythonの基礎知識を学習します.その後,気象データとその他のオープンデータを掛け合わせたデータ分析について理解します.時間の都合上,全てを十分に説明できる時間はありませんが,次の内容の理解のために一つ一つのプログラムを丁寧に読み進めてください.全てのpythonのプログラミングの「いろは」をすべて解説しているわけではありませんので,よく分からないことがあれば自身でGoogleなどで調べてみてください.

早速,Pythonの基礎について学びましょう.

1.1 Jupyter Notebookの起動

Jupyter Notebook(ジュピター・ノートブック)は,Pythonの手軽なインタラクティブ環境として利用できます.この講義では,このJupyter Notebookの環境を使いながら学習を進めて行きます.まずは,Jupyter NotebookでPythonのコードを記述し実行することから学びましょう.一部,Pythonの基本的なプログラミングについても学習しますが,十分な時間はありませんので各自お気に入りの書籍を見つけて勉強してください.

まずは,Jupyter Notebookを起動してみましょう.macOSの場合はターミナル,Windowsの場合はAnacoda Promptの中で,

$ jupyter notebook

と実行することでJupyter Notebookが起動します.また,左下のWindowsのスタートボタンから,「すべてのプログラム」→「Anaconda3 (64bit)」→「Jupyter Notebook (Anaconda3)」でも起動することができます.

fig1.jpg

図1: Jupyter Notebookのホーム画面

Jupyter Notebookのホーム画面には,幾つかのフォルダやファイルが表示されていると思いますが,Windowsの場合,ローカルディスクの中の「C¥:Users¥(ユーザー名)」の位置となります.今回,皆さんに配布するテキスト一式も,そのフォルダの配下に保存して下さい.拡張子が「.ipynb」となっているファイルが,Jupyter Notebookの出力形式となっています.

新規でNotebookを開く場合には,下の図のように,

fig7.jpg

図2: Jupyter Notebookの新規ファイルの作成

ホーム画面上の右上の「New」をクリックして,「Python3」をクリックします.すると次のように,

fig8.jpg

図3: Jupyter Notebookの新規ファイルができた!

Jupyter Notebookによるpythonプログラミングの環境ができあがります.ここの上にpythonコードを書いていきます.ブラウザ上でプログラミングができることに驚きますね.

1.2 Jupyter NotebookによるPythonの実行

Jupyter Notebookを使えばPythonプログラミングの実行は簡単です.


・(Step 1) セルを追加して(上の「+ボタン」を押す)
・(Step 2) コードを入力して
・(Step 3) 実行する(上の「Runボタン」を押す)


の手順ですぐに実行できます.まずは,画面に「Hello, World!」と出力させてみましょう.Pythonでは,print文を使います.

In [1]:
print("Hello, World!")
Hello, World!

と打ちます.上記の文頭にIn [ ] :と書かれたセルをクリックすると,pythonコードを入力することができます.pythonでは,画面に文字を表示する場合には,printという関数を用います.上にある「Run」ボタンを押してみて下さい.結果がその下に表示されると思います.簡単ですね.

fig2.jpg

図4: Jupyter Notebook上でのpythonコードの実行

新たなコードを追加するときには,左上の「+」ボタンをクリックすると新しいセルができあがります.

もちろん,pythonでは文字を表示するだけでなく計算もできます.

In [2]:
# 足し算
print(1+2)
# かけ算
print(3*4)
# 割り算
print(5/6)
# べき乗
print(7**8)
3
12
0.8333333333333334
5764801

コードの中の#から始まる行はコメントアウトで無視されます(コメント文といいます).後日見た時に思い出せるようにコメント文をつけておくと良いです.

作成されたNotebookは,画面左上の「File」から「Save as」をクリックして,ファイル名を入力することで保存できます.また,「Download as」をクリックすることで,様々な形式(html等)でも保存できます.

fig9.jpg

図5: Jupyter Notebookの保存

Jupyter Notebookでpythonを実行する最低限の作法は以上となります.Jupyter notebookに記載されたコードは上から順番に実行する必要があります.上に記載されたコードが前提となって処理が進められていきます.皆さんも手を動かしながら,適宜,コードを変更しながら処理を進めてみてください.

1.3 Pythonの基礎

Jupyter環境でコードを実行する方法がわかったところで、Pythonの基本をさらに続けます.

1.3.1 変数

まず変数から学びます。以下は,messageという名前の変数に,「python」という文字列を格納し,print関数でその変数に格納されている値を表示させるというコードです.文字列を作成するときは,シングルクォーテーションまたはダブルクォーテーションで囲みます.

In [3]:
# pythonという文字をmessageという変数に割り当てて画面に表示します.
message = 'python'
print(message)
python

CやFORTRANなどのプログラミング言語では,変数を扱うときに整数なのか文字なのかを「型」として設定し,これから利用するという宣言をする必要があります.しかし、pythonには変数の型宣言が基本的に必要ないので,変数を使いたいときに値を代入するだけで使えます.

文字列の後ろに「[番号]」を指定すると、文字列の一部を取り出すことができます。これをインデックスと言います.インデックスは「0」から始まります.3つ目の文字を取り出したいならばインデックスは「2」となりますので注意しましょう.

In [4]:
# 1文字ずつ画面に表示します.インデックスは0から始まります.
print(message[0])
print(message[1])
print(message[2])
print(message[3])
print(message[4])
print(message[5])
p
y
t
h
o
n

さて、先ほどは変数に文字を割り当てましたが,もちろん数字を割り当てることもでき,その変数を使って変数同士の演算(足し算など)も可能です.

In [5]:
# 変数dataに1を割り当てて,2,3,4,5…,9,10と順番に足していきます.
data = 1
print(data)
data = data + 2
print(data)
data = data + 3
print(data)
data = data + 4
print(data)
data = data + 5
print(data)
data = data + 6
print(data)
data = data + 7
print(data)
data = data + 8
print(data)
data = data + 9
print(data)
data = data + 10
print(data)
1
3
6
10
15
21
28
36
45
55

1から順番に10まで足し算をした結果が出力されました.ここでは変数の名前をdataとしましたが,どんな名前でも構いません.何か変数を作成するときは,可能な限り分かりやすい名前で作成しましょう.

1.3.2 リストと辞書

次は,リストについて説明します.リストとは複数の値をひとまとめにして扱うための仕組みです.他のプログラム言語で言うところの配列のようなものです.データ分析では配列のような複数の値を一緒に扱うことが多いため,リストは頻繁に使われます.

以下は,1から10まで数字が並んでいるデータを作っています.

In [6]:
# 1から10まで数字が並んだリストdatalistを作り画面に表示します.
datalist = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
print(datalist)
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

Pythonでリストを表現するには,全体を $[$ と $]$で囲んでカンマで区切ります.先頭からn番目の要素は,「変数名[n]」のように表記することで取得できます.たとえば,datalistという要素の先頭はdatalist[0],2番目はdatalist[1]です.文字列の取り出しのときと同様に,インデックス番号は0から始まります.

In [7]:
# リストdatalistの中の2番めの数を取り出して表示します.
print('2番目の数:', datalist[1])
2番目の数: 2

また,リストの要素数はlen関数を使って,len(datalist)のように表記すると取得できます.

In [8]:
# len関数でリストの要素の数を出力します.
print('要素数:', len(datalist))
要素数: 10

また,リスト型と似たものに,辞書型があります.辞書型を使うと,キーと値をペアにして複数の要素を管理することができます.Pythonで辞書を表現するには,全体を $\{$ と $\}$ で囲んで,{キー:値}のようにコロン区切りで表記します.キーは整数だけではなく,文字列でも指定できます.またリストとは違って,順番は特に関係ありません.

次の例のように,「札幌が気温20℃,相対湿度40%」「仙台が気温25℃,相対湿度45%」「東京が気温35℃,相対湿度50%」などのように,指定した何らかのキーに対して値を設定させたいときに使います.値を設定するときは,辞書データ[キー名]というように表記します.以下では,辞書型のデータを用意した後に,東京というキーを参照して,それに対応する50が表示されているところです.

In [9]:
# 辞書型では,辞書データとキー名を紐付けます.
dic_data = {'札幌':   [20, 40], 
            '仙台':   [25, 45], 
            '東京':   [35, 50], 
            '名古屋': [40, 30], 
            '大阪':   [35, 45], 
            '福岡':   [30, 45],
            '沖縄':   [25, 60]}
# 東京の辞書データを取り出す場合
print(dic_data['東京'])
# 東京の辞書データの中のインデックス番号1(相対湿度)を取り出す場合
print(dic_data['東京'][1])
[35, 50]
50

以上がリストと辞書の簡単な解説になります。

1.3.3 条件分岐

次に,条件分岐を説明します.条件分岐とは,何かの条件で処理を分岐することで,if文を使います.ifの横に指定した条件式(真偽の判定式)を満たしている場合(True)は,該当の文(はじめにある:からelse:の手前まで)が実行され,そうでない場合(False)は,else:以下が実行されます.つまり条件を満たすかどうかによって,処理を2分岐できるのです.

以下の処理は,数字の「5」がdatalistというリストの中に入っているかどうかを判定する例です.5 in datalistが判定式です.datalistの中に5が入っていれば,すぐ下に書いてある処理が実行されます.一方,このリストに5が入っていなければ,elseに飛びます.

Pythonのコーディングにおける注意点ですが,if文などを使うとき,次の行はインデント(字下げ)します.通常は半角スペース4つ分を置きます.Jupyter Notebookではif文を改行すると自動的に半角4文字分のスペースが入ります.

In [10]:
# if文の開始
if 5 in datalist:
    # 条件式の結果が真の場合
    print('True')
else:
    # 条件式の結果が偽の場合
    print('False')
# ここでif文は終わり
print('End of Program')
True
End of Program

if文の1行目「:」がif文の開始で、字下げがあるところまでが処理の対象です。elseの横にも「:」がありますが,これがelseの中身の処理が開始されることを意味し,これも字下げがあるところまでが処理の対象となります.他のプログラミング言語では,endなどがありますが,Pythonではそれを記載する必要がありません.字下げがなくなった個所から,if文の処理とは別の処理がはじまりますので注意してください.上の例では,最終行のprint('End of Program')はif文の処理の範囲外となります.

条件分岐のための比較演算子や真偽判定は,次のようにいろいろとあります.真の場合True,偽の場合Falseと表示されます.

In [11]:
#右と左の数字が同じかどうか?
datalist[0] == 1
Out[11]:
True
In [12]:
#右と左の数字が違うかどうか?
datalist[0] != 1
Out[12]:
False
In [13]:
#右の数字が左の数字より小さいかどうか?
datalist[0] < 10
Out[13]:
True
In [14]:
#右の数字が左の数字より大きいかどうか?
datalist[0] > 10
Out[14]:
False
In [15]:
#どちらも成り立っているかどうか?(AND)
(datalist[0] < 5) & (datalist[9] < 5)
Out[15]:
False
In [16]:
#どれか1つでも成り立っているかどうか?(OR)
(datalist[0] < 5) | (datalist[9] < 5)
Out[16]:
True

1.3.4 繰り返し

次に,繰り返し処理の構文を説明します.これはforを使います.for文は,リストデータなどからデータを1つずつ取り出し,データがなくなるまで繰り返し処理を実行します.

先のdatalistのリストに対して,先頭から順番に(1から)データを取り出し,データがなくなる(10まで)まで繰り返し処理(取り出した数字の表示と足し算)を実行しています.つまり,1から10までを足し算した結果となります.

In [17]:
#初期値の設定
total = 0

# for 文の開始
for num in datalist:
    # 取り出した数を足す
    total = total + num
# ここでfor文の終わり
    
# 最後に合計を表示
print(total)
55

処理の初めは1をnumに入れており、0+1=1となります。次に2をnumに入れて1+2=3となり、次に3を取り出して3+3=6となり・・・,この処理を10になるまで繰り返します.ここでfor文が終わり,最終的な合計値を表示しています.なお,このfor文もif文と同じように,「:」の以下からfor文がはじまり,字下げがされているところまでがfor文の処理対象となりますので注意してください.

繰り返し処理をするには,for文以外にwhileがあります.while文は,条件が成り立っている間は、ずっと繰り返し処理する構文です. 次の例は,変数numの値を表示して,1ずつ加えていき,その値が10より大きくなった時点で,処理を終えるというものです.なお,こちらもif文やfor文と同じように:の行でwhile文がはじまり,字下げがあるところまでが処理の対象となります.

In [18]:
# 初期値の設定
num = 1 
total = 0
# while文の開始
while num <= 10:
    #numをtotalに足す
    total = total + num
    #numを1足す
    num = num + 1
# ここでwhile文の終わり
    
# 最後に合計を表示
print(total)
55

1.3.5 関数

関数は,一連の処理をひとまとめにする仕組みです.関数を作成すると,同じような処理を何度か実行したいときに便利です.また,処理をまとめておくと,後でコードを修正するときにも便利です.

下記に示すsea_level_pressure関数の例は,3つの数字(気圧p1と気温t1と標高z1)をインプット(これを引数といいます)として,海面更正気圧p0の結果を返す関数です.気圧は様々な高度で計測されていますが,上空に行けば行くほど気圧が下がるため,地上天気図を作成するためには海面補正をする必要があります.これが海面更正気圧です.海面更正気圧は,

$p_0 = p_1 \displaystyle{\left( \frac{T_0 - \Gamma z_1 }{ T_0 } \right)^{-g/R_d \Gamma}}$

によって計算できます.ここで,海面更正気圧$p_0$は標高0mにおける気圧p0です.気圧$p_1$,気温$t_1$,標高$z_1$は観測点1の気圧p_1,気温t_1,高度z_1です.$\Gamma$は気温減率$=$0.65$K$/100$m$です.$g$は重力加速度$=$9.81 $m/s^2$です.$R_d$は乾燥空気の気体定数$=$287.0 $J K^{-1} kg^{-1}$です.

書き方としては,defの後に関数名,引数があれば,()の中に,引数名を記述します.この引数が入力となって,returnで結果を返し(返り値といいます),これが出力になります.なお,このdefineifforwhileと同じように,「:」の以下からdefがはじまり,字下げがされているところまでがdef文の処理対象となります.

In [19]:
# 気圧の高度補正をする関数
def sea_level_pressure(p1, t1, z1):
    # 重力加速度
    g=9.81
    # 乾燥空気の気体定数
    rd=287.0
    # 気温減率
    gamma=0.65/100
    # ℃ -> K   
    t1=t1+273.15
    # z=0での気温
    t0=t1+gamma*z1
    # 気圧の高度補正式
    p0=p1*(((t0-gamma*z1)/t0)**(-g/(rd*gamma)))
    return p0

関数を実行することを,関数を呼び出すと言います.作成した関数を呼び出すときには,関数名を書いて,引数が必要なときには,引数を与えて,実行します.関数は引数なしでも設定できます.以下は,引数として,気圧p1は940hPa,気温t1は15℃,標高z1は500.0mを与えて,上の関数により海面更正気圧を計算しています.

In [20]:
# 観測点の地表面気圧 hPa
p1=940.0
# 観測点の気温 ℃
t1=15.0
# 観測点の標高 m
z1=500.0
print("海面更正気圧[hPa]:", sea_level_pressure(p1,t1,z1))
海面更正気圧[hPa]: 997.108240210493

1.3.6 Numpyの基礎

Numpy(ナンパイ)は,科学計算でもっともよく使われる基本的なライブラリです.多次元配列を処理することができるなど,機能的に優れているだけでなく,C言語で書かれたモジュールであるため処理が高速なのも特徴です.

ここでは、Numpyを次のようにしてインポートします. 1行目では,「as np」としているので,以降のプログラムでは,Numpyライブラリを「np.機能名」と表記することで使えます.機能はメソッドとも言います. そして2行目はマジックコマンド(Jupyter Notebookの中だけで有効)です.結果を小数点何桁まで表示するのかという指定をします.ここでは,小数第3位まで表示するようにしました.

In [21]:
# Numpyライブラリの読み込み
import numpy as np

# 小数第3位まで表示という意味
%precision 3
Out[21]:
'%.3f'

まずは、1から10までの配列を作成してみましょう.Numpyにおいて,配列はarrayオブジェクトとして構成されます.これは,np.arrayのように,「インポートしたときにasの部分に付けた名前」と「array」をピリオドでつなげた名称で指定します. 10個の要素を持つ配列を作成する例を以下に示します.

In [22]:
# リストの作成
datalist=[5, 8, 4, 3, 7, 1, 6, 10, 2, 9]
# Numpy配列の作成
data = np.array(datalist)
print(data)
[ 5  8  4  3  7  1  6 10  2  9]

Pythonにおいて,Numpyではないふつうの配列(リスト)に対して,すべての要素を係数倍にするには,forを使ったループ処理が必要となります.以下は要素を2倍にする例です.空のdatalist1を作って,datalistから取り出した要素を1つ1つ2倍して,datalist1の末尾に追加(appendメソッド)しています.

In [23]:
# for文で要素を2倍する場合
datalist1=[]
for element in datalist:
    datalist1.append(element*2)
print(datalist1)
[10, 16, 8, 6, 14, 2, 12, 20, 4, 18]

しかし,Numpyの場合は,たとえば2倍にするのであれば,次のように,配列に対して「*2」と記述するだけですべての要素が2倍になるので簡単です.

In [24]:
# numpyで要素を2倍する場合
print(data * 2)
[10 16  8  6 14  2 12 20  4 18]

それぞれの要素での掛け算や割り算も,for文などを使わずに簡単に計算できます.

In [25]:
# それぞれの要素同士での演算
print('掛け算:', data * data)
print('累乗:', data ** 2)
print('割り算:', data / data)
掛け算: [ 25  64  16   9  49   1  36 100   4  81]
累乗: [ 25  64  16   9  49   1  36 100   4  81]
割り算: [1. 1. 1. 1. 1. 1. 1. 1. 1. 1.]

データを並べ替えるには,sortメソッドを使います.デフォルトでは,昇順(小さい数字から大きい数字)になります.

In [26]:
# 現在の値を表示
print('そのまま:', data)

# ソートした結果を表示
data.sort()
print('ソート後:', data)
そのまま: [ 5  8  4  3  7  1  6 10  2  9]
ソート後: [ 1  2  3  4  5  6  7  8  9 10]

なお,sortメソッドは,元のデータ(data)を置き換えるので注意しましょう.降順(大きい数字から小さい数字)にしたい場合は,data[::-1].sort()のように,スライスを使って操作します.スライスはPythonの機能で,[n:m:s]のように記述すると,要素の中の「n番目からm-1番目を,sずつ飛ばして取り出す」という意味になります.インデックス番号のnmは,0から始まることに注意してください.nmを省略したときは「すべて」という意味になります.またsが負のときは先頭からではなく,末尾から取り出すことを意味します.つまり,[::-1]は,「末尾から1つずつ取り出す」という意味になります.

In [27]:
print("末尾から要素を1つずつ取り出す",data[::-1])
print("5番目から9番目までの要素を順に取り出す",data[5:10:1])
print("最初から最後まで1つ飛びで取り出す",data[::2])
末尾から要素を1つずつ取り出す [10  9  8  7  6  5  4  3  2  1]
5番目から9番目までの要素を順に取り出す [ 6  7  8  9 10]
最初から最後まで1つ飛びで取り出す [1 3 5 7 9]

Numpyのarrayデータは,minメソッドやmaxメソッドを呼び出すことで,最小値や最大値なども求めることができます.cumsumというメソッドは積上(前から順に足し上げていく)演算です.

In [28]:
# 最小値を計算します.
print('Min:', data.min())
# 最大値を計算します.
print('Max:', data.max())
# 合計を計算します.
print('Sum:', data.sum())
# 積み上げを計算します.
print('Cum:', data.cumsum())
Min: 1
Max: 10
Sum: 55
Cum: [ 1  3  6 10 15 21 28 36 45 55]

Numpyを使うと行列計算もできます. まずは,行列の作成方法から説明します.次の例は,0〜8までの数字を3×3行列で表現するものです.arange関数は指定した連続した整数を発生する機能を持ちます.arrange(9)とした場合、0から8までの整数を発生します.それをreshape関数で3×3の行列に分割しています. これで変数array1に3×3の行列が作られます.再びデータを1次元に戻す場合には,ravelメソッドを使います.

In [29]:
# 1行×9列の行列(0~8まで)を生成します.
print(np.arange(9))
# 3行×3列の行列(0~8まで)を生成します.
array1 = np.arange(9).reshape(3,3)
print(array1)
# 再び1行9列のデータに戻します.
print(array1.ravel())
[0 1 2 3 4 5 6 7 8]
[[0 1 2]
 [3 4 5]
 [6 7 8]]
[0 1 2 3 4 5 6 7 8]

行列から,行や列のみを抜き出したいときは,「[行範囲:列範囲]」のように表記します.それぞれの範囲は,「開始インデックス,終了インデックス」のように,カンマで区切って指定します.開始インデックスや終了インデックスを省略したときは,それぞれ「最初から」「末尾まで」という意味になります.たとえば,次のように[0,:]を指定すると,「行は1行目」「列はすべて」という意味になるので,1行目のすべての列を取り出すことができます.

In [30]:
# 1行目を取り出します.
print(array1[0,:])
# 1列目を取り出します.
print(array1[:,0])
[0 1 2]
[0 3 6]

行列の掛け算をしてみましょう.この計算方法がわからない方は,線形代数の復習をしてください.まずは,掛け算する対象とする行列を作成しましょう.次の例では、3×3の行列を作成し,変数array2に代入しています.この行列と,先のarray1の行列を掛け算してみましょう. 行列の掛け算では,dot関数を使います.間違えて*を使うと,行列の掛け算ではなく、それぞれの要素を掛け算してしまうので注意しましょう.

In [31]:
array2 = np.arange(9,18).reshape(3,3)
print(array2)
# 行列の掛け算
array3 = np.dot(array1, array2)
print(array3)
# 行列の要素同士の掛け算
array4 = array1*array2
print(array4)
[[ 9 10 11]
 [12 13 14]
 [15 16 17]]
[[ 42  45  48]
 [150 162 174]
 [258 279 300]]
[[  0  10  22]
 [ 36  52  70]
 [ 90 112 136]]

1.3.7 Pandasの基礎

Pandas(パンダス)は,Pythonで機械学習を行う前のデータの前処理をするときに便利なライブラリです.さまざまなデータのさまざまな加工処理をスムーズに柔軟に実施することができ,表計算データの抽出検索などの操作ができるようになります.具体例を挙げると,顧客データの中からある条件(女性だけ)を満たす行を抽出したり,ある軸(男女別など)を設定してそれぞれの平均値(身長,体重など)を算出したり,データを結合するなどの操作ができます.

まず,Pandasのライブラリをインポートします.「import pandas as pd」としてPandasをインポートしているので,「pd.機能名」と表記することでPandasライブラリを使えるようになっています.以下ではさらに,二次元の配列を扱うときのDataFrameライブラリをインポートします.

In [32]:
import pandas as pd

DataFrameオブジェクトは2次元の配列(辞書型)です.下記は,番号住所年齢名前の4つの列を持つデータ構造を示した例です.Jupyter notebookでは,データフレーム形式は表形式できれいに表示されます.

In [33]:
#辞書の用意
dictdata1 = {'地点番号':['18273','62078','61111','46106','44132','14163','52146'],
               '地点名':['根室','大阪','舞鶴','横浜','東京','札幌','高山'],
               '気温':[1.0,9.1,4.8,8.5,6.6,1.2,2.3],
               '相対湿度':[79,81,98,65,76,65,90],
               '都道府県':['北海道','大阪','京都','神奈川','東京','北海道','岐阜']}
print(dictdata1)

#PandasのDataframeに割り当てる
dfdata1 = pd.DataFrame(dictdata1)
dfdata1
{'地点番号': ['18273', '62078', '61111', '46106', '44132', '14163', '52146'], '地点名': ['根室', '大阪', '舞鶴', '横浜', '東京', '札幌', '高山'], '気温': [1.0, 9.1, 4.8, 8.5, 6.6, 1.2, 2.3], '相対湿度': [79, 81, 98, 65, 76, 65, 90], '都道府県': ['北海道', '大阪', '京都', '神奈川', '東京', '北海道', '岐阜']}
Out[33]:
地点番号 地点名 気温 相対湿度 都道府県
0 18273 根室 1.0 79 北海道
1 62078 大阪 9.1 81 大阪
2 61111 舞鶴 4.8 98 京都
3 46106 横浜 8.5 65 神奈川
4 44132 東京 6.6 76 東京
5 14163 札幌 1.2 65 北海道
6 52146 高山 2.3 90 岐阜

一番左列に表示されている0, 1, 2, 3, 4, 5, 6の値は,インデックスの値です.データフレームはインデックスを変更したり,インデックスとして文字を指定したりすることもできます. 次のようにインデックスを指定すると,dictdata1の値に対して新しいインデックスを指定したdfdata1というデータフレームを作ることができます.

In [34]:
dfdata1 = pd.DataFrame(dictdata1,index=['a','b','c','d','e','f','g'])
dfdata1
Out[34]:
地点番号 地点名 気温 相対湿度 都道府県
a 18273 根室 1.0 79 北海道
b 62078 大阪 9.1 81 大阪
c 61111 舞鶴 4.8 98 京都
d 46106 横浜 8.5 65 神奈川
e 44132 東京 6.6 76 東京
f 14163 札幌 1.2 65 北海道
g 52146 高山 2.3 90 岐阜

データフレームDataFrameは,さまざまな行列操作ができます.行列の転置のように,行と列を入れ替える場合には,.Tメソッドを使います.

In [35]:
# 転置
dfdata1.T
Out[35]:
a b c d e f g
地点番号 18273 62078 61111 46106 44132 14163 52146
地点名 根室 大阪 舞鶴 横浜 東京 札幌 高山
気温 1 9.1 4.8 8.5 6.6 1.2 2.3
相対湿度 79 81 98 65 76 65 90
都道府県 北海道 大阪 京都 神奈川 東京 北海道 岐阜

特定の列だけを指定したいときは、データの後にその列名を指定します。複数の列を指定したいときは、それらをPythonのリストの形式で指定します。

In [36]:
# 列名の指定(1つの場合)
dfdata1[['気温']]
Out[36]:
気温
a 1.0
b 9.1
c 4.8
d 8.5
e 6.6
f 1.2
g 2.3
In [37]:
# 列名の指定(1つの場合)これでもよい
dfdata1.気温
Out[37]:
a    1.0
b    9.1
c    4.8
d    8.5
e    6.6
f    1.2
g    2.3
Name: 気温, dtype: float64
In [38]:
# 列名の指定(複数の場合)
dfdata1[['地点番号', '気温']]
Out[38]:
地点番号 気温
a 18273 1.0
b 62078 9.1
c 61111 4.8
d 46106 8.5
e 44132 6.6
f 14163 1.2
g 52146 2.3

データフレームでは,特定の条件を満たすデータだけを取り出したり,複数のデータを結合したりすることもできます.次の例は,データのうち,住所東京のみのデータを抽出する例です.この処理は,dfdata1['住所'] == '東京'Trueであるデータをすべてdfdata1から抽出するもので,フィルターの役割を果たしています.

In [39]:
# 条件(フィルター)
dfdata1[dfdata1['都道府県'] == '北海道']
Out[39]:
地点番号 地点名 気温 相対湿度 都道府県
a 18273 根室 1.0 79 北海道
f 14163 札幌 1.2 65 北海道

複数の条件をつけることもできます.以下は,住所が東京か大阪であるデータを抽出しています.

In [40]:
# 複数条件(フィルター)
dfdata1[(dfdata1['都道府県'] == '東京') | (dfdata1['都道府県'] == '大阪')]
Out[40]:
地点番号 地点名 気温 相対湿度 都道府県
b 62078 大阪 9.1 81 大阪
e 44132 東京 6.6 76 東京

以下は,相対湿度が60~80%であるデータを抽出しています.

In [41]:
# 複数条件(フィルター)
dfdata1[(dfdata1['相対湿度'] >= 60 ) & (dfdata1['相対湿度'] <= 80)]
Out[41]:
地点番号 地点名 気温 相対湿度 都道府県
a 18273 根室 1.0 79 北海道
d 46106 横浜 8.5 65 神奈川
e 44132 東京 6.6 76 東京
f 14163 札幌 1.2 65 北海道

データフレームは,必要のない列や行を削除したり,他のデータフレームと結合したりすることもできます。ある特定の列や行を削除するにはdropメソッドを実行します。axisパラメータに軸を指定します。axis=0が行」「axis=1が列」です.なお,このaxisパラメータは他の場面でも使うので覚えておいてください.

行削除の場合:1つ目の引数に削除したい行のインデックスをリストとして指定します.axisパラメータには「0を指定します.

列の削除の場合:1つ目の引数に削除したい列名をリストとして指定します.axisパラメータには「1を指定します.

次の例は、年齢列を削除する例です.

In [42]:
dfdata1.drop(['相対湿度'], axis = 1)
Out[42]:
地点番号 地点名 気温 都道府県
a 18273 根室 1.0 北海道
b 62078 大阪 9.1 大阪
c 61111 舞鶴 4.8 京都
d 46106 横浜 8.5 神奈川
e 44132 東京 6.6 東京
f 14163 札幌 1.2 北海道
g 52146 高山 2.3 岐阜

データフレーム同士は結合することもできます.データ分析ではさまざまなデータがある場合に,それらを結合して分析することは多々あります.まずは例として,結合先のデータフレームを,次のようにdfdata2という変数で用意します。

In [43]:
# 別のデータの準備
dictdata2 = {'地点番号':['62078', '61111','46106','14163','52146'],
               '緯度':[34.68, 35.45, 35.44, 43.06, 36.16],
               '降水':['なし','あり','なし','あり','あり']}
dfdata2=pd.DataFrame(dictdata2)
dfdata2
Out[43]:
地点番号 緯度 降水
0 62078 34.68 なし
1 61111 35.45 あり
2 46106 35.44 なし
3 14163 43.06 あり
4 52146 36.16 あり

そして,はじめに作ったdfdata1とこのdfdata2を結合して,新たにdfdata3を作ります. 結合するにはmergeメソッドを使います.キーを明示しないときは,自動で同じキーの値であるものを見つけて結合します.この例の場合,キーは地点番号です.'62078', '61111','46106','14163','52146'が共通であるため,それが合致するデータが結合されます.

In [44]:
# データのマージ
dfdata3=pd.merge(dfdata1,dfdata2)
dfdata3
Out[44]:
地点番号 地点名 気温 相対湿度 都道府県 緯度 降水
0 62078 大阪 9.1 81 大阪 34.68 なし
1 61111 舞鶴 4.8 98 京都 35.45 あり
2 46106 横浜 8.5 65 神奈川 35.44 なし
3 14163 札幌 1.2 65 北海道 43.06 あり
4 52146 高山 2.3 90 岐阜 36.16 あり

データフレームのデータは,ソートすることもできます.値だけではなく,インデックスをベースにソートできます.まずは先ほど作ったサンプルデータdfdata2を次のように定義します.ソートの効果がわかりやすくなるよう,わざとデータを適当な順で並べてあります.

In [45]:
dfdata2 = pd.DataFrame(dictdata2,index=['e','d','b','a','c'])
dfdata2
Out[45]:
地点番号 緯度 降水
e 62078 34.68 なし
d 61111 35.45 あり
b 46106 35.44 なし
a 14163 43.06 あり
c 52146 36.16 あり

インデックスでソートするには,次のようにsort_indexメソッドを実行します.

In [46]:
# indexによるソート
dfdata2.sort_index()
Out[46]:
地点番号 緯度 降水
a 14163 43.06 あり
b 46106 35.44 なし
c 52146 36.16 あり
d 61111 35.45 あり
e 62078 34.68 なし

値でソートする場合には,次のようにsort_valuesメソッドを使います.

In [47]:
# 値によるソート、デフォルトは昇順
dfdata2.地点番号.sort_values()
Out[47]:
a    14163
b    46106
c    52146
d    61111
e    62078
Name: 地点番号, dtype: object

DataFrameオブジェクトでは,データを集計することできます.さらにgroupbyメソッドを使うと,ある特定の列を軸とした集計ができます.以下は「降水の列」を軸として,気温の平均を算出する例です.スコア平均を計算するにはmeanメソッドを使います.他にも,最大値を計算するmaxメソッド最小値を計算するminメソッドなどもあります.

In [48]:
# データのグループ集計
dfdata3.groupby('降水')['気温'].mean()
Out[48]:
降水
あり    2.766667
なし    8.800000
Name: 気温, dtype: float64

DataFrameオブジェクトでは、相関係数のような統計値も計算できます.以下は「緯度の列」と「気温の列」を軸として、緯度と気温の相関係数を算出している例です.相関係数0に近ければ無相関1に近ければ正の相関-1に近ければ負の相関となります.

In [49]:
# 緯度と気温の相関係数
print(dfdata3['緯度'].corr(dfdata3['気温']))
-0.7202142813485314

1.3.8 datetimeの基礎

特に気象データを分析する際には,日付の処理をすることがあります.datetime(デイトタイム)をインポートすると,日時(日付や時刻)の処理が可能となります.datetimeライブラリでは,datetime.datetimedatetime.timedeltaオブジェクトがよく使われます.以下のように,インポートします.

In [50]:
from datetime import datetime, timedelta

まず,現在の日時を得るには,datetime.now()メソッド使います.

In [51]:
dt_now = datetime.now()
print(dt_now)
2021-08-06 12:41:12.451006

もちろん,任意の日付,時刻を設定することもできます.

In [52]:
dt = datetime(2021, 5, 13, 13, 0, 0, 0)
print(dt)
2021-05-13 13:00:00

dtから日付(何年何月何日)だけを取り出すにはdate()メソッドを使います.年のみを取り出す場合はyearメソッド,月のみを取り出す場合はmonthメソッド,日にちのみを取り出す場合はdayメソッドとなります.

In [53]:
print(dt.date())
print(dt.year)
print(dt.month)
print(dt.day)
2021-05-13
2021
5
13

日付から曜日を取り出した場合には,weekday()メソッドを使います.月曜日が0,火曜日が1,・・・土曜日が5,日曜日が6と出力されます.

In [54]:
weekdaylist = ['月曜日', '火曜日', '水曜日', '木曜日', '金曜日', '土曜日', '日曜日']
print(dt.weekday())
print(weekdaylist[dt.weekday()])
3
木曜日

dtから時刻(何時何分何秒)だけを取り出すにはtime()メソッドを使います.時間のみを取り出す場合はhourメソッド,分のみを取り出す場合はhourメソッド,秒のみを取り出す場合はhourメソッドとなります.

In [55]:
print(dt.time())
print(dt.hour)
print(dt.minute)
print(dt.second)
13:00:00
13
0
0

datetimeオブジェクト同士の引き算からtimedeltaオブジェクトを作ることができます.2つの日時の時間差経過時間を表したりできます.以下は,dtdt_nowの時間差,経過時間を表します.

In [56]:
td = dt - dt_now
# 時間差,経過時間
print(td)
# 日数で表す
print(td.days)
# 秒数で表す
print(td.seconds)
-85 days, 0:18:47.548994
-85
1127

timedeltaオブジェクトを使うと,1週間後の日付や4時間後の時刻などを簡単に計算して取得できます.1週間後の日時を表したい場合には,次のようにweeks=1days=7とする.

In [57]:
td_1w = timedelta(days=7)
print(dt + td_1w)
2021-05-20 13:00:00
In [58]:
td_4h = timedelta(hours=4)
dt_4h = dt + td_4h
print(dt_4h)
2021-05-13 17:00:00

strftime()メソッドを使うと,datetimeオブジェクトから日時の情報を任意の書式の文字列として変換できます.

%d : 0埋めした10進数で表記した月中の日にち
%m : 0埋めした10進数で表記した月
%y : 0埋めした10進数で表記した西暦の下2桁
%Y : 0埋めした10進数で表記した西暦4桁
%H : 0埋めした10進数で表記した時 (24時間表記)
%I : 0埋めした10進数で表記した時 (12時間表記)
%M : 0埋めした10進数で表記した分
%S : 0埋めした10進数で表記した秒
%f : 0埋めした10進数で表記したマイクロ秒(6桁)
%A : ロケールの曜日名
%a : ロケールの曜日名(短縮形)
%B : ロケールの月名
%b : ロケールの月名(短縮形)
%j : 0埋めした10進数で表記した年中の日にち(正月が'001')
%U : 0埋めした10進数で表記した年中の週番号 (週の始まりは日曜日)
%W : 0埋めした10進数で表記した年中の週番号 (週の始まりは月曜日)

例えば,以下のように様々な日付や日時の表現ができます.

In [59]:
print(dt.strftime('%Y-%m-%d %H:%M:%S'))
print(dt.strftime('%y%m%d'))
print(dt.strftime('%A, %B %d, %Y'))
2021-05-13 13:00:00
210513
Thursday, May 13, 2021

文字列ではなく数値として月や日にちを得たい場合には,int()で整数型にすればよいです.

In [60]:
month_num = int(dt.strftime('%m'))
print(month_num)
5

1.3.9 Matplotlibの基礎

データ分析をする上で,対象となるデータを可視化することはとても重要です.単に数字を眺めているだけでは,データに潜む傾向がなかなか見えなかったりしますが,データをビジュアル化することで,データ間の関係性なども見えてきます.ここでは,主にMatplotlibSeabornを使って,データを可視化する基本的な方法を身につけましょう.

Matplotlib(マットプロットリブ)では、描画に関するほとんどの機能が「pyplot.機能名」で提供されています.この機能を使うためには,「import matplotlib.pyplot as plt」とインポートし,「matplotlib.pyplot.機能名」とフルネームで書くのではなく「plt.機能名」と略記できるようにしています.SeabornはMatplotlibのグラフを,さらにきれいにするライブラリです.インポートするだけでグラフがきれいになり,また,いくつかの追加のスタイルを指定できるようになります.以下の「%matplotlib inline」は,Jupyter Notebook上にグラフを表示するためのマジックコマンドです.

In [61]:
# Matplotlib と Seabornの読み込み
# pyplotにはpltの別名で実行できるようにする
import matplotlib.pyplot as plt
import seaborn as sns

# Jupyter Notebook上でグラフを表示させるために必要なマジックコマンド
%matplotlib inline

Matplotlabでは、さまざまなグラフを描けますが,まずは,データ分析でよく使う散布図から始めましょう.散布図は,2つの組み合わせデータに対して,x−y座標上に点をプロットしたグラフです.plt.plot(x, y, 'o')で描写でき,最後の引数はグラフの形状を指定するもので'o'は点で描くという意味です.その他の動作については,コード中のコメントを参考にしてください.散布図を描くと,2変数の関係性などが見えてきます.

In [62]:
# x軸のデータ(0から1までの一様分布の乱数生成)
x = np.random.rand(100)

# y軸のデータ(0から1までの一様分布の乱数生成)
y = np.random.rand(100)

# グラフの大きさ指定(10を変更してみてください)
plt.figure(figsize=(10, 10))

# グラフの描写
plt.plot(x, y, 'o')

#以下でも散布図が描ける
#plt.scatter(x, y)

# タイトル
plt.title('Sin')
# Xの座標名
plt.xlabel('X')
# Yの座標名
plt.ylabel('Y')

# grid(グラフの中にある縦線と横線)の表示
plt.grid(True)

連続した値を与えれば,plotによる描画は点ではなく曲線として示すことができます.たとえば次の例は,時系列など連続した点を曲線として描くものです.なお,linspace(0, 2*np.pi,100)は0から$2 \pi$までの数を100個に分割した数字のリストを作成するものです.

In [63]:
# x軸のデータ(0から2πまでを100刻み(均等割)のデータを作成する)
x = np.linspace(0, 2*np.pi, 100)

# y軸のデータ(sinの計算)
y = np.sin(x)

# グラフの大きさ指定(20や6を変更してみてください)
plt.figure(figsize=(20, 6))

# グラフの描写
plt.plot(x, y, label='Label')
# label=とlegendでラベルをつけることが可能
plt.legend()

# タイトル
plt.title('Sin')
# Xの座標名
plt.xlabel('X')
# Yの座標名
plt.ylabel('Y')

# grid(グラフの中にある縦線と横線)の表示
plt.grid(True)

subplotを使うと,グラフを複数に分けることができます.以下は,2行1列のグラフを作成し,1番目と2番目と番号を指定して表示する例です.

In [64]:
# グラフの大きさを指定
plt.figure(figsize=(20, 6))

# 2行1列のグラフの1つ目
plt.subplot(2,1,1)
# -10から10まで100個に分割した数字のリストを作成する
x = np.linspace(-10, 10,100)
# 1つ目の時系列
plt.plot(x, np.sin(x)) 
# Xの座標名
plt.xlabel('X')
# Yの座標名
plt.ylabel('Y')
# grid(グラフの中にある縦線と横線)の表示
plt.grid(True)

# 2行1列のグラフの2つ目
plt.subplot(2,1,2)
# -10から10まで100個に分割した数字のリストを作成する
y = np.linspace(-10, 10,100)
# 2つ目の時系列
plt.plot(y, np.sin(2*y)) 
# Xの座標名
plt.xlabel('X')
# Yの座標名
plt.ylabel('Y')
# grid(グラフの中にある縦線と横線)の表示
plt.grid(True)

subplotを使って,1枚の図に2つのグラフを表示することもできます.以下は,1行1列のグラフを作成し,2つのグラフを同じ図の中に表示しています.2つ目のグラフは1つ目のグラフのx軸と同じ物を使うので,twinx()メソッドを使います.1つ目のグラフはax1というインスタンスで,2つ目のグラフはax1というインスタンスで管理され,2つ目のグラフのy軸は第2軸として図の右側に現れます.逆に,y軸を共有して,x軸を2軸にする時はtwiny()メソッドを使います.2つのグラフは振幅が10倍の大きさで異なりますが,y軸を2軸で表現することによって2つのデータを比較しやすくなります.

In [65]:
# グラフの大きさを指定
plt.figure(figsize=(20, 6))
# 1行1列のグラフの1つ目
ax1=plt.subplot(1,1,1)
# 2つ目のグラフのx軸を共有する
ax2 = ax1.twinx()

# x軸の設定
x = np.linspace(-10, 10,100)

# 1つ目のグラフ
ax1.plot(x, np.sin(x), color='blue', label='data 1') 

# 2つ目のグラフ
ax2.plot(x, 10*np.sin(2*x), color='red', label='data 2') 

ax1.set_title('data 1 and data 2')
ax1.set_xlabel("X")
ax1.set_ylabel("Y1")
ax2.set_ylabel("Y2")

ax1.legend(loc='upper left')
ax2.legend(loc='upper right')
Out[65]:
<matplotlib.legend.Legend at 0x1b1b1434dc8>

1.4 まとめ

● Pythonの基礎として,変数,リスト,辞書,条件分岐,繰り返し,関数の基本を学びました.
● 数値計算の基本的な演算のためのNumpyの基本を学びました.
● データの前処理のためのPandasの基礎を学びました.
● 日付処理のためのdatetimeの基礎を学びました.
● 作図のためのMatplotlibの基礎を学びました.

次の「アメダス気象データ分析チャレンジ!(Python版) 1日目~実践編~」のための基本的なpythonの文法について理解できと思います.必要があれば,ここに戻ってきて復習してください.また,困ったことがあれば,google検索などでインターネット上の情報を調べてみましょう.大抵のことは検索で事足ります.