ITやAIに関するちょっとしたメモ

AIやIoTをはじめITに関して記録しておきたいことをメモ的に書いていきます。

【一気通貫で理解するPythonの基礎・基本】辞書型、コツがわかると難しくない階層の辞書型、REST API→JSON→辞書型

Pythonでデータをまとめて扱うときには、前回の一気通貫で簡単なグラフ描画含めて説明したようなリストという配列形式を利用できます。しかし、Pythonにはそれとは異なる特徴のデータ形式もあります。それがDictと呼ばれる辞書型です。

辞書型が便利な用途は、データを「キー」と「バリュー(値)」のセットで扱いたいときです。ここでは、Pythonの辞書型について、基本の基本、辞書型にリスト型の配列を加える、階層にした辞書型データの扱い、さらには近年流行りのREST APIで得たJSONの出力を辞書型に変換して特定の値だけを切り出して表示する例まで、小出しにせず、まとめて一気に説明します。目次を見て、自分に必要なところだけ読んでいただいても構いません(実行するときはAPIのところを除き先頭からやってください)。

f:id:ColdSnap:20191114162931p:plain

辞書型データの作成

もっとも基本的な辞書型データの作成方法です。{ }で囲って、要素のキー(ラベルのようなもの)と対応する値を、「"キー":値」のセットで並べていきます。

data1 = { "apple":1, "orange":2, "banana":3, "grape":4, "kiwi":5 }
print (data1)
{'apple': 1, 'orange': 2, 'banana': 3, 'grape': 4, 'kiwi': 5}

print(辞書名)以外に、次の方法でも辞書のデータを全て表示することができます。items()は辞書型データのキーと値のセットを扱うときに使うメソッドです。

for i in data1.items():
  print(i)
('apple', 1)
('orange', 2)
('banana', 3)
('grape', 4)
('kiwi', 5)

ちなみに、キーだけを扱いたいときはkeys()、値だけを扱いたい時にはvalues()を使います。

for i in data1.keys():
  print(i)
apple
orange
banana
grape
kiwi
for i in data1.values():
  print(i)
1
2
3
4
5

キーによる辞書内のデータ検索

辞書形式は、キーを索引代わりに使って該当するキーに対応する値を検索することができます。

print(data1["apple"])
1

キーに対応する値が存在するかどうかを調べることもできます。値そのものより、あるかどうかだけを知りたい、という用途には有用です。

print("apple" in data1.keys())
True
print("melon" in data1.keys())
False

辞書に値を追加

辞書に値を追加することができます。キーと対応する値を指定してやります。

data1["strawberry"] = 6
print(data1)
{'apple': 1, 'orange': 2, 'banana': 3, 'grape': 4, 'kiwi': 5, 'strawberry': 6}

辞書の中の値を変更

辞書のデータを変更するときは、キーと対応する値を指定してやります。これ、追加と全く同じです。つまり、指定したキーが無ければ追加して、あれば変更してしまうという仕様になっています。辞書は同じキーのものを複数入れることができないので、こうなっているのでしょう。

data1["strawberry"] = 7
print(data1)
{'apple': 1, 'orange': 2, 'banana': 3, 'grape': 4, 'kiwi': 5, 'strawberry': 7}

辞書の中のデータ削除

辞書の中のデータを削除するには、pop()というメソッドを使い、()の中に削除したいデータのキーを指定します。

data1.pop("banana")
print(data1)
{'apple': 1, 'orange': 2, 'grape': 4, 'kiwi': 5, 'strawberry': 7}

ソートして表示

辞書型のデータは順序を保証しません。もし、順序をきれいに並べ替えたければ、出力時にsorted()でソートします。

print(sorted(data1.items()))
[('apple', 1), ('grape', 4), ('kiwi', 5), ('orange', 2), ('strawberry', 7)]

降順に並べ替える場合は、reverse=Trueを指定します。

print(sorted(data1.items(), reverse=True))
[('strawberry', 7), ('orange', 2), ('kiwi', 5), ('grape', 4), ('apple', 1)]

辞書型のデータをリスト形式(配列)へ変換

辞書型のデータをリスト形式に変換して入れることができます。list()を使います。

data2 = list(data1.items())
print(data2)
[('apple', 1), ('orange', 2), ('grape', 4), ('kiwi', 5), ('strawberry', 7)]

辞書型にリスト形式(配列)のデータを追加

キーに対応させる値は、個別の値ではなく、配列にしたい場合も考えられます。その場合は、辞書にリスト形式のデータを追加することができます。

data1["watermelon"] = [8, 9, 10]
print(data1)
{'apple': 1, 'orange': 2, 'grape': 4, 'kiwi': 5, 'strawberry': 7, 'watermelon': [8, 9, 10]}

辞書型の中のリスト形式のデータを参照

配列として登録したリストを、キーから検索して取り出して表示するにはつぎのようにできます。

print(data1["watermelon"])
[8, 9, 10] 

さらに、そのリストの一つ目の要素だけが必要、という場合は次のように指定します。リスト形式の場合、配列の要素の番号は1からではなく0から数えます。

print(data1["watermelon"][0])
8

辞書の階層化(ネスト化)

辞書は階層にすることができます。章立てを作るような感覚でしょうか。ここでは、5つの果物をさらに、「fruits」というキーをつけてひとまとめにできるようにします。

{'fruits':
 {'apple': 1,
 'orange': 2,
 'banana': 3,
 'grape': 4,
 'kiwi': 5}
}

data4 = {"fruits": { "apple":1, "orange":2, "banana":3, "grape":4, "kiwi":5 }}
print(data4)
{'fruits': {'apple': 1, 'orange': 2, 'banana': 3, 'grape': 4, 'kiwi': 5}}

階層化した辞書データを参照

このように階層化したデータの参照はどうすればいいのでしょう?実は、最初の階層のキー、2層目のキーのキーと、上位の層から絞り込んでいきたい順にキーを並べて特定できるようにしてやればこの順番でたどって値を参照できます。階層が深くなってもこのパターンは同じです。

print(data4["fruits"]["apple"])
1

階層化した辞書にデータを追加

それでは、階層化した辞書へのデータ追加はどうすればいいのでしょう?これも階層がないときのパターンと基本的には同じなのですが、辞書のキーの指定のところを階層の数だけ並べることでたどって特定できるようにしてやります。

data4["fruits"]["lemon"] = 6
print(data4)
{'fruits': {'apple': 1, 'orange': 2, 'banana': 3, 'grape': 4, 'kiwi': 5, 'lemon': 6}}

階層に辞書を追加

上記の例は、fruitsという辞書だけですが、ここにさらにvegetableという辞書型のデータをまるごと追加してみましょう。

data4.update({'vegetables': {'carrot': 1, 'onion': 2, 'pumpkin': 3, 'potato': 4, 'parsley': 5}})
print(data4)
{'fruits': {'apple': 1, 'orange': 2, 'banana': 3, 'grape': 4, 'kiwi': 5, 'lemon': 6}, 'vegetables': {'carrot': 1, 'onion': 2, 'pumpkin': 3, 'potato': 4, 'parsley': 5}}

Yahoo!JapanのREST APIを呼び出してみる

ここでは辞書型の応用として、REST APIの呼び出しと、そこから取得したJSON形式のデータを辞書型に変換して表示させるという、簡単な例を示したいと思います。使うのは、Yahoo!Japanが公開しているAPIで、ここから、郵便番号(〒105-0011)に該当する位置情報(緯度・経度)を引っ張り出します。尚、このプログラムの実行には、Yahoo!Japanの「デベロッパーネットワーク」に登録して個別のAPIキーを取得する必要があります。APIキーの取得は単なる手続きで難しくありませんので、このWebサイトからたどって取得ください。
developer.yahoo.co.jp

ここではまず、requestとjsonの2つのライブラリーをimportする必要があります。前者はURLでデータを扱うためのもの、後者はJSON形式を扱うためのものです。

import requests
import json

URL = "https://map.yahooapis.jp/search/zip/V1/zipCodeSearch?query=105-0011&output=json&appid=<ここは各自がYahoo! DeveloperからAPIキーを取得する必要があります>"
resp = requests.get(URL)
dict= json.loads(resp.text)
print("位置情報:", dict['Feature'][0]['Geometry']['Coordinates'])
位置情報: 139.74816650,35.65757726

API, JSON, 辞書のネストと一見難しそうですが、核となる部分だけを凝縮すれば、この程度のコード量で実現できます。まずURLを呼び出してAPIから取得したJSON形式のデータを辞書形式に変換して入れます。あとは、そこから深い階層にある位置情報の部分だけを階層ごとにキーを並べて特定していき(リストで入っているところは配列の番号で指定し)、print()でその値を出力しているだけです。

ちなみに、JSON形式で取得して辞書型に入れたデータ全体は以下になります。(目がくらみます)

{'ResultInfo': {'Count': 1, 'Total': 1, 'Start': 1, 'Status': 200, 'Description': '', 'Copyright': '', 'Latency': 0.009}, 'Feature': [{'Id': 'd30c42d8a8cb04ee730d1e5846ea2a8a', 'Gid': '', 'Name': '〒105-0011', 'Geometry': {'Type': 'point', 'Coordinates': '139.74816650,35.65757726'}, 'Category': ['郵便番号', '町域郵便番号'], 'Description': 'Yahoo!郵便番号検索', 'Style': [], 'Property': {'Uid': '2281d2dd75f52ee9ef0ba86149310b0cf710aedd', 'CassetteId': '3ee7f7f5fe1ef2267e319b15168e37d3', 'Country': {'Code': 'JP', 'Name': '日本'}, 'Address': '東京都港区芝公園', 'GovernmentCode': '13103', 'AddressMatchingLevel': '6', 'PostalName': '東京都港区芝公園', 'Station': [{'Id': '22584', 'SubId': '2258401', 'Name': '御成門', 'Railway': '都営三田線', 'Exit': 'A1', 'ExitId': '3607', 'Distance': '396', 'Time': '4', 'Geometry': {'Type': 'point', 'Coordinates': '139.750445,35.659440'}}, {'Id': '22712', 'SubId': '2271201', 'Name': '芝公園', 'Railway': '都営三田線', 'Exit': 'A4', 'ExitId': '4055', 'Distance': '496', 'Time': '6', 'Geometry': {'Type': 'point', 'Coordinates': '139.749697,35.654488'}}, {'Id': '22815', 'SubId': '2281501', 'Name': '大門(東京都)', 'Railway': '都営浅草線/都営大江戸線', 'Exit': 'A6', 'ExitId': '4541', 'Distance': '541', 'Time': '6', 'Geometry': {'Type': 'point', 'Coordinates': '139.753831,35.656964'}}]}}]}

最後に

辞書型は、Pythonで「キー」と「バリュー」でデータを扱う上で欠かせないデータ形式です。現代のITでは、キー・バリュー・ストア(KVS: Key-Value Store)と呼ばれるタイプのNoSQLデータベースが一般化していますし、上記の例で示したようにRSET APIなどでJSONと呼ばれる形式のデータを利用するときにも辞書型に変換して扱うことが多くあります。よって、辞書型はPythonを学ぶ上で重要な基礎知識であり、実用性の高いデータ形式でもあります。ここではよくある一次階層の辞書の基本だけというのではなく、階層型やJSON形式からの変換についても実例を交えてまとめて説明してみましたので、参考になるところがあれば幸いです。