確率分布の期待値・分散・母関数まとめ~離散分布~
今回は、統計検定1級/準1級の対策として、各確率分布の期待値・分散・母関数について整理しました。
1. 離散一様分布
2. ベルヌーイ分布
3. 二項分布
定義
確率pのベルヌーイ試行をn回行ったときに、ある事象が何回起こるかを表す確率分布のこと。 ※n=1のとき、ベルヌーイ分布になります。
確率関数
期待値
分散
確率母関数
積率母関数(モーメント母関数)
5. 超幾何分布
6. 幾何分布
定義
成功確率pのベルヌーイ試行を繰り返し実行し、初めて成功するまでの失敗回数をXとしたとき、Xは幾何分布に従う。
※初めて成功するまでの試行回数(失敗回数+1)と定義する場合もある
確率関数
期待値
分散
確率母関数
積率母関数(モーメント母関数)
7. 負の二項分布
定義
成功確率pのベルヌーイ試行を繰り返し実行し、r回成功するまでの失敗回数をXとしたとき、Xは負の二項分布に従う。※r=1のとき、幾何分布になります。
確率関数
期待値
分散
確率母関数
積率母関数(モーメント母関数)
導出方法
多くの参考書において、これらの導出は、数式の途中計算や公式等の前提知識の説明が省略されていることが多いため、理解に時間がかかると感じていました。
今回、自分用に整理したものを、動画にしてみました。途中の流れを細かく説明しています。必要に応じてご確認ください。
youtu.be
対策本
統計検定1級/準1級の対策本としては、以下の書籍があります。
こちらの書籍は、検定の範囲内のトピックが幅広く網羅されていますが、数式や解説が省略されている個所が多い印象です。あくまでも、出題範囲のトピックを確認するための用途として使用し、詳細の内容はインターネット等で確認し理解を深めるのが良いと思います。まとめ
確率分布の期待値・分散・母関数について整理しました。また、導出方法についてまとめた動画および、対策本について紹介しました。
本記事が、統計検定の対策を進める上で、お役に立てば幸いです。
Pythonでコイントスをするアプリを作ってみた|Pyinstaller
Pythonでコイントスをするアプリを作ってみましたので、画像イメージとソースコードをこちらに残します。
画面イメージ
ソースコード
cointoss.py
#!/usr/bin/env python # coding: utf-8 import os import sys import time import random title=''' ■ ■ ■ ■ ■■■■ ■ ■■■■ ■■ ■ ■■■ ■ ■■■■ ■ ■ ■■ ■■ ■ ■■ ■■ ■ ■■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■■ ■■ ■ ■ ■ ■ ■ ■ ■ ■■■■■■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■■ ■■ ■ ■■ ■■■■ ■ ■ ■ ■ ■■■■■ ■ ■■■■ ■ ■ ■ ■ ■ ■■■■ ■■■■ ■ ■ ■■■ ■■■■ ■■■■ ■■■■ ■■■■ ■■ ■ ■■ ■■ ■ ■■ ■■ ■ ■■ ■■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■■ ■■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■■ ■ ■■ ■■ ■ ■ ■ ■■ ■■ ■■ ■ ■ ■ ■ ■■■■ ■■■■ ■ ■ ■ ■■ ■■■■ ■■■■ ■■■■ ''' omote=''' ■■■■■■■■■■■■■■■ ■■■ ■■■ ■■ ■■ ■■ ■■ ■■ ■ ■■ ■ ■ ■■■■■■■■■■■■■■■■ ■ ■ ■■ ■ ■ ■■■■■■■■■■■■■■ ■ ■ ■■ ■ ■ ■■ ■ ■ ■■■■■■■■■■■■■■■■■■ ■ ■ ■■■■ ■ ■ ■■■ ■ ■■ ■ ■ ■■ ■■ ■■ ■ ■ ■■■■■ ■■■ ■ ■ ■■■ ■ ■■ ■ ■ ■ ■ ■■ ■ ■ ■■■■■■ ■■■ ■ ■ ■■■■■■ ■■■ ■ ■ ■ ■■ ■ ■■ ■■ ■■ ■■ ■■■ ■■■ ■■■■■■■■■■■■■■■ ''' ura=''' ■■■■■■■■■■■■■■■ ■■■■■■■■■■■■■■■■■■■ ■■■■■■■■■ ■■■■■■■■■■■ ■■■■■■■■■■ ■■■■■■■■■■■ ■■ ■■■ ■■■■■■■■■■■■■■■■■■■■■■■■■ ■■■■■ ■■■■■■ ■■■■■ ■■■■ ■■■■ ■■■■■■ ■■■■■ ■■■■■■ ■■■■■ ■■■■ ■■■■ ■■■■■■ ■■■■■ ■■■■■■ ■■■■■■■■■■■ ■■■■■■■■■■■■ ■■■■ ■■■■■ ■■■■■■■■■■■ ■■■■■■■■■■■■ ■■■ ■■■■ ■■■■■■■■■ ■ ■■■ ■■■■■■ ■■■■■■■ ■■■ ■ ■■■■■■ ■■■ ■■■■■ ■■■■■■■■ ■■■ ■■■■ ■■ ■ ■■■■■■■ ■■■■■ ■■■■■ ■■■ ■■■■■ ■■■■■■■■■■■■■■■■■ ■■■■■■■■■■■■■■■■■■■■■ ■■■■■■■■■■■■■■■■■■■ ■■■■■■■■■■■■■■■ ''' def toss(): for i in range(4): # print("コイントス中:") print(" ") print(omote) time.sleep(0.1) os.system('cls') print(" ") print(ura) time.sleep(0.1) os.system('cls') def main(): counter_total = 0 counter_omote = 0 counter_ura = 0 while(1): print(title) print('') print('Enterキーを押してコイントスを開始します。終了するときは、"q"キーを押します') x = input() os.system('cls') if x == 'q': break else: toss() counter_total += 1 result = random.randint(0, 1) if result == 0: counter_omote += 1 print("結果:") print(omote) else: counter_ura += 1 print("結果:") print(ura) print('実施回数:'+str(counter_total) + ' 表:'+str(counter_omote) + ' 裏:'+str(counter_ura)) x = input() os.system('cls') if __name__ == '__main__': main() sys.exit(0)
注意:文字をアスキーアート風に描いています。ブログ画面の表示上ズレていますが、テキストエディタにコピペすれば、以下の様にきれいに見えると思います。
exeファイルへの変換方法
Pythonプログラムをexeファイル化することで、Pythonをインストールしていない環境でも、アイコンをクリックするだけで簡単にPythonプログラムを実行できるようになります。
exeファイル化する際には、Pyinstallerを使用します。やり方は以下の動画で公開していますので、よければこちらをご確認ください。
youtu.be
また、Pyinstallerで作成したexeファイルのエラー発生時の対処方法について、別記事に公開していますので、こちらも必要に応じてご確認ください。
dskevin.hatenablog.com
Pythonでレトロ2Dゲームを作ってみた|Pyxel|ゲームプログラミング
PythonのPyxelというライブラリを使って、レトロゲームを作ってみました。
youtu.be
おにぎりくんが爆弾を避けながらビールを獲得していくというシンプルなゲームです。※おにぎりくんは、親戚の子供向けに作成した架空のキャラクターです。
Pyxel紹介
Pythonのライブラリの1つです。ゲームを作るために必要な基本的な機能が用意されているので、ゲームプログラミング初心者の方でも、短時間でゲームを実装できます。ゲーム内で使用するアイコンや音楽も自分で作ることができます。
Pyxelのサイト:
https://github.com/kitao/pyxel/blob/master/README.ja.md
ソースコード
サイトに公開されているサンプルコードを基に、見よう見まねで実装しました。オブジェクト指向プログラミングの練習にもなりました。
処理が冗長だったり、わかりづらかったりする部分はあるかと思いますが、一旦、作りたい仕様は実現できたので良しとします。
# -*- coding: utf-8 -*- from collections import deque, namedtuple from random import randint import pyxel import time Point = namedtuple("Point", ["w", "h"]) UP = Point(-16, 16) DOWN = Point(16, 16) RIGHT = Point(-16, 16) LEFT = Point(16, 16) class App: def __init__(self): pyxel.init(160, 120, caption="Onigiri-kun Loves Beer") pyxel.load("my_resource.pyxres") self.direction = RIGHT #START FLAG self.START = False #GAMEOVER FLAG self.GAMEOVER = False self.end_bgm_flg = 1 # Score self.score = 0 self.items_got = 0 self.bombs_got = 0 self.total_items = 0 # Starting Point self.player_x = 37 self.player_y = 38 self.player_vy = 0 self.item = [((i+4) * 60, randint(6, 104), True) for i in range(4)] self.sp_item = [((i+1) * 950, randint(6, 104), True) for i in range(2)] self.bomb = [((i+3) * 77, randint(6, 104), True) for i in range(3)] self.timebar = 99 pyxel.playm(0, loop=True) pyxel.run(self.update, self.draw) def update(self): if pyxel.btnp(pyxel.KEY_Q): pyxel.quit() # enter key to start if pyxel.btn(pyxel.KEY_ENTER) or pyxel.btn(pyxel.GAMEPAD_1_START): self.START = True if self.timebar < 0: self.GAMEOVER = True if self.GAMEOVER is True: pyxel.stop() if self.GAMEOVER and (pyxel.btn(pyxel.KEY_ENTER or pyxel.btn(pyxel.GAMEPAD_1_START))) : self.reset() if not self.START or self.GAMEOVER: return self.update_player() for i, v in enumerate(self.item): self.item[i] = self.update_item(*v) for i, v in enumerate(self.sp_item): self.sp_item[i] = self.update_sp_item(*v) for i, v in enumerate(self.bomb): self.bomb[i] = self.update_bomb(*v) self.timebar -= 0.2415 def update_player(self): if pyxel.btn(pyxel.KEY_LEFT) or pyxel.btn(pyxel.GAMEPAD_1_LEFT): self.player_x = max(self.player_x - 4, 0) self.direction = LEFT if pyxel.btn(pyxel.KEY_RIGHT) or pyxel.btn(pyxel.GAMEPAD_1_RIGHT): self.player_x = min(self.player_x + 4, pyxel.width - 16) self.direction = RIGHT if pyxel.btn(pyxel.KEY_UP) or pyxel.btn(pyxel.GAMEPAD_1_UP): self.player_y = max(self.player_y - 4, 0) self.direction = UP if pyxel.btn(pyxel.KEY_DOWN) or pyxel.btn(pyxel.GAMEPAD_1_DOWN): self.player_y = min(self.player_y + 4, pyxel.height - 16) self.direction = DOWN def draw(self): if self.GAMEOVER: if self.end_bgm_flg == 1: if (self.bomb[0][2] is False and abs(self.bomb[0][0] - self.player_x) < 48 and abs(self.bomb[0][1] - self.player_y) < 48) \ or (self.bomb[1][2] is False and abs(self.bomb[1][0] - self.player_x) < 48 and abs(self.bomb[1][1] - self.player_y) < 48) \ or (self.bomb[2][2] is False and abs(self.bomb[2][0] - self.player_x) < 48 and abs(self.bomb[2][1] - self.player_y) < 48): pyxel.play(3, 6) else: pyxel.play(3, 7) time.sleep(1) self.end_bgm_flg = 0 MESSAGE =\ """ FINISH PUSH ENTER RESTART """ pyxel.text(51, 40, MESSAGE, 1) pyxel.text(50, 40, MESSAGE, 7) return # bg color pyxel.cls(12) # time bar pyxel.rect( 50, 2, 90 if self.timebar > 90 else self.timebar, 4, 11 if self.timebar > 60 else 10 if self.timebar > 40 else 9 if self.timebar > 20 else 8 ) # draw item for x, y, is_active in self.item: if is_active: pyxel.blt(x, y, 0, 16, 0, 16, 16, 12) # draw special item for x, y, is_active in self.sp_item: if is_active: pyxel.blt(x, y, 0, 16, 16, 16, 16, 12) # draw bomb for x, y, is_active in self.bomb: if is_active: pyxel.blt(x, y, 0, 32, 0, 16, 16, 12) # draw player pyxel.blt( self.player_x, self.player_y, 0, 0, 16 if (self.item[0][2] is False and abs(self.item[0][0] - self.player_x) < 12 and abs(self.item[0][1] - self.player_y) < 12) or (self.item[1][2] is False and abs(self.item[1][0] - self.player_x) < 12 and abs(self.item[1][1] - self.player_y) < 12) or (self.item[2][2] is False and abs(self.item[2][0] - self.player_x) < 12 and abs(self.item[2][1] - self.player_y) < 12) or (self.item[3][2] is False and abs(self.item[3][0] - self.player_x) < 12 and abs(self.item[3][1] - self.player_y) < 12) else 32 if (self.bomb[0][2] is False and abs(self.bomb[0][0] - self.player_x) < 24 and abs(self.bomb[0][1] - self.player_y) < 24) or (self.bomb[1][2] is False and abs(self.bomb[1][0] - self.player_x) < 24 and abs(self.bomb[1][1] - self.player_y) < 24) or (self.bomb[2][2] is False and abs(self.bomb[2][0] - self.player_x) < 24 and abs(self.bomb[2][1] - self.player_y) < 24) else 48 if (self.sp_item[0][2] is False and abs(self.sp_item[0][0] - self.player_x) < 48 and abs(self.sp_item[0][1] - self.player_y) < 48) or (self.sp_item[1][2] is False and abs(self.sp_item[1][0] - self.player_x) < 48 and abs(self.sp_item[1][1] - self.player_y) < 48) else 0, self.direction[0], self.direction[1], 12, ) # print score s = "Score: {:>3}".format(self.score) pyxel.text(5, 4, s, 1) pyxel.text(4, 4, s, 7) s = "Beer: {:>1}".format(self.items_got) + " Bomb: {:>1}".format(self.bombs_got) pyxel.text(5, 110, s, 1) pyxel.text(4, 110, s, 7) if not self.START: START_TEXT1 ="PUSH ENTER KEY" START_TEXT2 =": PLAYER" START_TEXT3 =": 100 pt / time +++" START_TEXT4 =": 10 pt / time +" START_TEXT5 =": -50 pt / time ---" pyxel.text(50, 23, START_TEXT1, 1) pyxel.text(49, 23, START_TEXT1, 7) pyxel.text(56, 43, START_TEXT2, 1) pyxel.text(55, 43, START_TEXT2, 7) pyxel.blt(37, 55, 0, 16, 16, 16, 16, 5) pyxel.text(56, 61, START_TEXT3, 1) pyxel.text(55, 61, START_TEXT3, 7) pyxel.blt(37, 73, 0, 16, 0, 16, 16, 5) pyxel.text(56, 79, START_TEXT4, 1) pyxel.text(55, 79, START_TEXT4, 7) pyxel.blt(37, 91, 0, 32, 0, 16, 16, 5) pyxel.text(56, 97, START_TEXT5, 1) pyxel.text(55, 97, START_TEXT5, 7) return def update_item(self, x, y, is_active): if is_active and abs(x - self.player_x) < 12 and abs(y - self.player_y) < 12: is_active = False self.score += 10 self.items_got += 1 self.timebar += 3 self.player_vy = min(self.player_vy, -8) pyxel.play(3, 4) self.total_items += 1 if self.GAMEOVER is False: x -= 3 if x < -10: if is_active is True: self.total_items += 1 if self.timebar < 0: x = 999999999999999 else: x += 240 y = randint(6, 104) is_active = True return (x, y, is_active) def update_sp_item(self, x, y, is_active): if is_active and abs(x - self.player_x) < 12 and abs(y - self.player_y) < 12: is_active = False self.score += 100 self.timebar += 10 self.items_got += 1 self.player_vy = min(self.player_vy, -8) pyxel.play(3, 5) self.total_items += 1 if self.GAMEOVER is False: x -= 5 if x < -10: if is_active is True: self.total_items += 1 if self.timebar < 0: x = 999999999999999 else: x += 1900 y = randint(6, 104) is_active = True return (x, y, is_active) def update_bomb(self, x, y, is_active): if is_active and abs(x - self.player_x) < 12 and abs(y - self.player_y) < 12: is_active = False self.score -= 50 self.timebar -= 50 self.bombs_got += 1 self.player_vy = min(self.player_vy, -8) pyxel.play(3, 6) if self.GAMEOVER is False: x -= 2 if x < -10: if self.timebar < 0: x = 999999999999999 else: x += 231 y = randint(6, 104) is_active = True return (x, y, is_active) def reset(self): #GAMEOVER FLAG self.GAMEOVER = False # Score self.score = 0 self.items_got = 0 self.bombs_got = 0 self.total_items = 0 # Starting Point self.player_x = 42 self.player_y = 60 self.player_vy = 0 self.item = [((i+5) * 60, randint(6, 104), True) for i in range(4)] self.sp_item = [((i+1) * 950, randint(6, 104), True) for i in range(2)] self.bomb = [((i+4) * 77, randint(6, 104), True) for i in range(3)] self.timebar = 99 self.end_bgm_flg = 1 pyxel.playm(0, loop=True) App()
プログラム実行ファイル
実行ファイルは、Google ドライブ上にzipファイル形式で公開してます。以下のリンクから、zipファイルをダウンロードできます。
origiri-kun_loves_beer_v1.0.zip - Google ドライブ
ダウンロードの完了後、解凍ソフト等を使用して、zipファイルを展開します。
※Google ドライブなのでファイル改ざんの可能性は無いと思いますが、念のため、ウイルスソフトでファイルをスキャンすることをお勧めします。
exeファイルをダブルクリックすると、ゲームが起動されます。是非遊んでみてください。
まとめ
投稿が遅くなりましたが、夏休みの成果物として、プログラムのコードおよびプログラム実行ファイルを公開しました。
また、実際に作業している様子を動画にしていますので、Pyxelを使って実際にゲームを作ってみたいという方の参考になれば幸いです。
youtu.be
最後まで読んでいただき、ありがとうございました。
【Python/Pandas】Webページ上のデータを読み込んで、動く棒グラフを作成する方法|AKB48 シングル選抜回数
こんにちは。今回は、Web上に公開されているAKB48歴代シングルにおける選抜メンバーの一覧表を読み込んで、メンバーごとの選抜回数の推移を集計し、動く棒グラフ(Bar Chart Race)で可視化したいと思います。
AKB48のディスコグラフィー/選抜メンバーの一覧 - エケペディア
事前準備
今回も、ツールはPythonを使用します。Pythonのセットアップ方法は過去の記事を参照ください。
データサイエンティストを目指す方向け Pythonセットアップ方法(Windows版) - Kevin's Data Analytics Blog
また、Pythonを使って動く棒グラフを作成するためには、PCにいくつかのソフトウェアをインストールする必要があります。こちらも手順については、過去の記事でまとめていますので、併せてご参照ください。
Pythonで動く棒グラフを作成する方法|Bar Chart Race - Kevin's Data Analytics Blog
Webページ上のデータの読み込み
Pythonの実行環境を開いたら、まず、Pandasライブラリをインポートします。
import pandas as pd
Pandasライブラリが用意しているpd.io.html.read_html()という関数を使用すると、指定したURLから表形式のデータだけを抜き出してくれます。
target_url = 'https://48pedia.org/AKB48%E3%81%AE%E3%83%87%E3%82%A3%E3%82%B9%E3%82%B3%E3%82%B0%E3%83%A9%E3%83%95%E3%82%A3%E3%83%BC/%E9%81%B8%E6%8A%9C%E3%83%A1%E3%83%B3%E3%83%90%E3%83%BC%E3%81%AE%E4%B8%80%E8%A6%A7'
tables = pd.io.html.read_html(target_url)
実行すると、tablesという変数に、URL内の表データがリスト形式で保存されるので、1番目(リストの番号は0始まりなので0)を指定して、dfという変数に格納します。
df = tables[0]
変数名だけで実行すると、変数の中身をみることができます。
df
データの読み込みが出来ました。
データの加工
動く棒グラフを書くためにデータを加工します。
不要な列の削除
del 関数を使用して、列を削除します。今回は、一番左の「所属」列と一番右の「選抜回数」列は使用しないので、それぞれ、列番号を指定して削除します。
del df[df.columns[61]] del df[df.columns[0]]
不要な行の削除
「名前」もしくは「定員」から始まる行を削除します。
df = df[df[df.columns[0]]!='名前'] df = df[df[df.columns[0]]!='定員']
列名の加工
列名が3階層(発売年、枚数、曲名)になっているため、1階層にまとめます。また、元のサイトでは、曲名は縦書きですが、これを横書きにするために、記号文字を変換します。replace関数を使って、「|」→「ー」、「 `」→「、」とします。
df.columns = [df.columns[i][0]+'年\n'+df.columns[i][1]+'. '+df.columns[i][2] for i in range(len(df.columns))] df.columns = [df.columns[i].replace('|','ー').replace(' `','、') for i in range(len(df.columns))]
データ値の変換
欠損値は選抜されなかったことを表すため「0」に、「●」と「★」は選抜されたことを表すため「1」に変換します。欠損値の置き換えは、fillna関数を使用します。
df = df.fillna(0).replace('●',1).replace('★',1)
行と列の入れ替え
元のデータは、左から右の順番で古いシングルから新しいシングルのデータが格納されています。動く棒グラフを作成するために、上から下に時系列が進むように行と列を入れ替えます。このような操作を行列の転置といい、データの変数に「.T」をつけて実行すると転置されます。また、転置前の行インデックスが転置後の列名になりますので、set_index関数を使用して、転置前に1列目のメンバーの名前をインデックスに指定します。
df = df.set_index(df.columns[0]).T
ここまでのデータ加工のイメージは以下のとおりです。
累積和の計算
最後に、cumsum関数を使用して、列ごとに縦方向に累積和を計算します。これにより各データの値は、該当行のシングル曲の時点における、それまでの合計の選抜回数を表します。
これで、動く棒グラフを作成するためのデータ準備ができました。
動く棒グラフの作成
加工後のデータを利用して、動く棒グラフを作成します。
今回はデータに日本語を含むので、japanize_matplotlibというPythonライブラリをインストールして、Pythonのグラフ描画機能を日本語データに対応させる必要があります。もし、まだインストールしていない場合は、コマンドプロンプトから以下のコマンドを実行します。
pip install japanize_matplotlib
Pythonの実行環境に戻って、ライブラリをインポートします。
import bar_chart_race as bcr import japanize_matplotlib
bar_chart_race関数に、先ほど加工したデータ「df」を指定して、動く棒グラフを作成します。パラメータの値はお好みで調整ください。
bcr.bar_chart_race( df=df ,fixed_max=True ,title='AKB48 歴代メンバー シングル選抜回数 ランキング推移 2006.02-2021.06 ' ,n_bars=27 ,bar_size=.8 ,period_label={'x': .975, 'y': .075, 'ha': 'right', 'va': 'center', 'size': 13} ,filter_column_colors=True )
グラフのカスタマイズ
スマホ表示用に動画を縦長にする
fig_kwargsというパラメタで動画サイズを指定します。スマホ用の動画は、横と縦の比率が9:16が適していますので、文字サイズとのバランスを考慮して、それぞれ1/2にして4.5:8とします。
bcr.bar_chart_race( df=df ,fixed_max=True ,title='AKB48 歴代メンバー シングル選抜回数 ランキング推移 2006.02-2021.06 ' ,n_bars=27 ,bar_size=.8 ,period_label={'x': .975, 'y': .05, 'ha': 'right', 'va': 'center', 'size': 11} ,fig_kwargs={'figsize': (4.5, 8), 'dpi': 72} ,filter_column_colors=True )
棒の色を指定する
例として、メンバーの所属チームに応じて、棒の色を変えてみたいと思います。
まずは、前準備として、Excelファイルに各メンバーの所属チームをまとめます。複数所属している場合は、代表的なチームを選んでいます。なお、メンバーの並び順をウェブから取得したテーブルとExcelファイルで一致させる必要がある点にご注意ください。
pd.read_excel関数を使用して、こちらのExcelファイルを読み込みます。
member_team=pd.read_excel('../data/member_team.xlsx')
「color」という列を追加して、「team」列の値に応じて色を指定します。replace関数を使用して、'A'などのチーム名を'red'などの色の名称に変えていきます。
member_team['color']=member_team['team'].replace('A','red').replace('K','lime').replace('B','aqua').replace('SKE','orange').replace('NMB','gold').replace('HKT','dimgrey').replace(4,'skyblue') # チーム不明のメンバーはwhiteで補完 member_team['color']=member_team['color'].fillna('white') member_team
filter_column_colorsをFalseにして、colorsに先ほど作成したデータの「color」列の値を指定します。
bcr.bar_chart_race( df=df ,fixed_max=True ,title='AKB48 歴代メンバー シングル選抜回数 2006.2-2021.6 ' ,n_bars=27 ,bar_size=.8 ,period_label={'x': .975, 'y': .05, 'ha': 'right', 'va': 'center', 'size': 11} ,fig_kwargs={'figsize': (4.5, 8), 'dpi': 72} ,filter_column_colors=False ,colors=member_team['color'] )
【初心者向け】Pandas入門 これだけは押さえておくべき基本操作|データ分析で必須
こんにちは、今回はPythonのライブラリの1つである、Pandasについて紹介したいと思います。
Pandasとは
データ分析用のライブラリで、表形式のデータを効率的に処理するための機能が多く提供されています。
実際のデータ分析作業のうち「データ前処理が8割を占める」とも言われるくらい、データ加工技術はデータ分析者にとって必須スキルであると言えます。また、Pythonでは機械学習用のライブラリもたくさん提供されているため、機械学習を適用する前のデータ加工も、PythonのライブラリであるPandasを使って行われることが多いです。
データソース
今回使用したデータは、以下のKaggleで公開されているポケモンの統計データを加工したものです。
Pokemon with stats | Kaggle
基本操作
Pandasライブラリのインポート
PythonでPandasを使うために、最初にライブラリをインポートします。
import pandas as pd
Pandasは慣習として、「pd」という短縮名でインポートします。
1. データファイルの読み込み(CSV/Excel/TSV)
CSVファイル
CSV形式のデータファイルを読み込む際には、pd.read_csvという関数を使用します。括弧の中には、対象のファイルのパスを記載します。
df = pd.read_csv('../data/Pokemon.csv')
上の例では、プログラムファイルが格納されているフォルダから1つ階層を上がって、「data」というフォルダ内にある「Pokemon.csv」を読み込んでいます。
もし、パスの記述方法についてあまり詳しくない場合は、プログラムファイルと同じフォルダ対象ファイルを置いて、ファイル名をシングルクォーテーション(’)で囲んで記述してもよいです。
df = pd.read_csv('Pokemon.csv')
また、pd.read_csvで読み込んだデータは、「=」の左側の変数に保存することができます。ここの変数名は自由に付けることが出来ます。こちらも慣習として、Pandasの表形式データを表す「データフレーム(DataFrame)」の略で「df」という変数名が使われることが多いです。
Excelファイル
Excel形式のデータを読み込む場合は、pd.read_excelという関数を使用します。使い方はpd.read_csvと同じです。
df = pd.read_excel('../data/Pokemon.xlsx')
TSVファイル
TSV形式のデータを読み込む場合も、CSV形式の場合と同じくpd.read_csv関数を使用します。ただし、オプションとして、「delimiter='\t'」を追加します。
df = pd.read_csv('../data/pokemon.txt', delimiter='\t')
TSV形式のデータは、データがタブで区切られているデータのことです。「delimiter」は区切り文字を意味し、「\t」タブ記号を意味します。
なお、pd.read_csvは、カンマやタブ以外で区切られたデータも読み込むことができ、例えば、「delimiter='XXX'」とすれば、XXXが区切り文字としてみなされます。
2. データの確認
データ全体
Jupyter notebookで、変数名だけ入力して実行すると、データフレームの中身を確認することが出来ます。
df
なお、末尾には、行数と列数も表示されています。このデータの場合、800行12列であることがわかります。
列名
「変数名.columns」で列名のリストを表示することが出来ます。
df.columns
特定の列の値
「変数名[’列名’]」で特定の列の値を表示することが出来ます。
df['Name']
また、複数の列名を指定する場合は、括弧を2重にして指定します。
df[['#','Name','Type 1']]
特定の行の値
「変数名.loc[行インデックス]」で特定の列の値を表示することが出来ます。行インデックスはデフォルトで0から順に整数が割り当てられています。
赤で囲んでいる部分が行インデックスです。
インデックスは0始まりなので、2行目を抽出したい場合は1を指定します。
df.loc[1]
また、複数の行を指定する場合は、複数列の指定と同様に括弧を2重にして指定します。
df.loc[[1,4,6]]
3. 行のソート
「変数名.sort_values('列名')」で特定の列をキーとして、行を辞書順または数値順に並べ替えることができます。
例えば、Name列で並べ替えたい場合は、次の様に実行します。
df.sort_values('Name')
降順に並べ替えたい場合は、「ascending=False」を付けます。
df.sort_values('Name', ascending=False)
また、[]で囲むことで複数の列をキーとして指定することができます。
df.sort_values(['Type 1', 'HP'])
デフォルトだと全て降順になりますが、昇順か降順かを別々に指定することもできます。例えば、Tpye 1を辞書の順に並べ変えて、HPを降順で大きい順に並べ替える場合のコードは以下のとおりです。
df.sort_values(['Type 1', 'HP']) , ascending=[True, False])
4. 列の追加・削除
pandasで読み込んだデータの列を追加することができます。
「変数名['新しい列名'] = 値」とします。
df['Test'] = 1
Testという列が追加されて、全ての値が1になっています。
新しい列の値として、それぞれの行で別々の値を設定することもできます。
例えば、各モンスターのHPからSpeedまでを全て足した値を、Totalという列名で追加する場合のコードは以下のとおりです。
df['Total'] = df['HP'] + df['Attack'] + df['Defense'] + df['Sp. Atk'] + df['Sp. Def'] + df['Speed']
列を削除する場合は、「del 変数名['削除する列名']」とします。
先ほど追加したTestという列を削除します。
del df['Test']
5. 列の並べ替え
列の順番を並べ替えることもできます。
ここでは、一番に右側に追加したTotal列を、HP列の前に移動させたいと思います。コードの例は以下のとおりです。
# 現在の列名をリスト形式にして、変数colsに保存 cols = list(df.columns)
# 変数colsの中身を表示 print(cols)
# 列名のリストをコピーし、表示したい順番に並べ替えて、別の変数cols_newに保存 cols_new = ['#', 'Name', 'Type 1', 'Type 2', 'Total', 'HP', 'Attack', 'Defense', 'Sp. Atk', 'Sp. Def', 'Speed', 'Generation', 'Legendary']
# 元の変数を上書き保存
df = df[cols_new]
Total列の順番が変更されました。
6. ファイルに保存(CSV/Excel/TSV)
データフレームをファイルに保存することができます。読み込み時と同様に、3種類のファイル形式への出力方法を紹介します。
CSVファイル
データフレームをCSVファイルに保存する場合は、to_csvという関数を使用します。括弧の中には、出力ファイルのパスを記載します。
df.to_csv('../data/Pokemon_v2.csv')
実行すると、CSVファイルが生成されます。
一番左のインデックス番号が不要な場合は、「index=False」を追加して実行します。
df.read_csv('../data/Pokemon_v2.csv', index=False)
まとめ
Pandasの基本操作について紹介しました。今回紹介したとおり、Pandasでは多くの操作について、1行から数行の短いプログラムで書くことができます。そのため、プログラミング初心者でもすぐに使えるようになると思います。
また、今回も、記事の内容を実演した様子を動画にしてアップしています。わかりづらい点があれば、こちらもあわせてご確認ください。
youtu.be
最後まで読んでいただきありがとうございました。
データ分析者が知らないと損するLinuxコマンド5選
今回は、データ分析者にオススメのLinuxコマンドを5つ紹介したいと思います。
はじめに
Linux環境
Linuxはサーバー向けのOSですが、近年は、GCPやvirtualboxなどの仮想マシン上にLinuxをインストールして使っている方も多いと思います。その中でも、簡単にLinuxを使う方法の1つとして、前回の記事で紹介したCygwinをインストールする方法があります。Cygwinを使うとWindowsのPC上でLinuxコマンドを実行することができます。まだの方は、是非インストールしてみてください。
dskevin.hatenablog.com
コマンド実行用のサンプルデータ
それぞれのコマンドの実行例は、以下のサイトで公開されているオープンデータを使用しています。
Fire Department Calls for Service | DataSF | City and County of San Francisco
Fire_Department_Calls_for_Service.csv 約2GB
※米国サンフランシスコ市における、緊急車両の出動要請のログデータ
1. headコマンド ~データの中身を確認~
説明
大きいサイズのデータファイルをExcelで開こうとすると、開くのに時間がかかったり、場合によってはフリーズが発生します。
Linuxのheadコマンドを使うと、テキストファイルの先頭10行を素早く表示することができます。
CSVファイルにも適用することができるので、大きいサイズのデータファイルの中身をざっと確認したい時に便利です。
head ファイル名
headコマンドは、-nオプションを使用することで、表示する行数を指定することも出来ます。
head -n 行数 ファイル名
また、別のファイルに保存したい場合は、以下のように「> 出力ファイル名」を付けて実行します。
head ファイル名 > 出力ファイル名
このように、Linuxでは実行するコマンドの最後に「> 出力ファイル名」を付けると、出力結果を別のファイルに保存することができます。
実行例
サンプルデータを用いて、実際にコマンドを実行してみます。
まずは、ファイルを置いてあるディレクトリに移動します。今回は、デスクトップに置いてあります。ここで、「userpc」の部分はログインユーザ名に置き換えてください。
cd /cygdrive/c/Users/userpc/Desktop/
headコマンドを実行します。
head Fire_Department_Calls_for_Service.csv
実行イメージは以下のとおりです。
ファイルの冒頭部分が表示されました。このように、どのような内容が含まれているか確認することが出来ます。ただし、今回のデータは1行が長く、改行されていて見づらいのです。そこで、先頭20行を別のファイルに保存してExcelで開いてみます。
head -n 20 Fire_Department_Calls_for_Service.csv > tmp_data.csv
tmp_data.csvが同じディレクトリに生成されるので、Excelで開いてみます。こちらは、ファイルのサイズが小さくなっているため、すぐに開けました。
このように、大きいサイズのデータの中身を確認したい時は、headコマンドを使うと時間を短縮することができます。
2. grepコマンド ~特定の行を抽出~
説明
grepコマンドも大きいサイズのファイルを扱う際に便利なLinuxコマンドです。
grepコマンドを使うと、指定した文字列を含む行だけを表示することができます。データ分析において、データファイルの中から、分析対象のデータを部分的に抽出するときに役に立ちます。
grep "キーワード" ファイル名
キーワードはシングルクォーテーションまたはダブルクォーテーションで囲みます。
複数のキーワードを含む行を抽出したい場合は、「|」を使って、再度grepコマンドを書きます。2つ目以降はファイル名は不要です。
grep "キーワード1" ファイル名 | grep "キーワード2" | grep "キーワード3" ...
実行例
Fire_Department_Calls_for_Service.csvの中から、"04/12/2021"と"Explosion"を含む行だけを抽出する場合は、以下のように実行します。
grep "04/12/2021" Fire_Department_Calls_for_Service.csv | grep "Explosion"
抽出した結果を別のファイルに出力する場合の実行例は、以下のとおりです。
grep "04/12/2021" Fire_Department_Calls_for_Service.csv | grep "Explosion" > explosion_20210412.csv
これで、2021年4月12日に対応したExplosion(爆発)による出動要請に関するレコードだけを抽出することができました。
3. cutコマンド ~特定の列を抽出~
説明
cutコマンドを使うと、CSVファイルの特定の列のみを抽出することができます。
分析に必要な列だけを残したいときに使用します。-f オプションに残したい列番号をカンマ区切りで複数指定できます。
cut -d , -f 列番号1,列番号2,... ファイル名
実行例
先ほど出力したCSVファイル(explosion_20210412.csv)から、1列目(Call Number)、4列目(Call Type)、5列目(Received DtTm)、17列目(City)、26列目(Call Type Group)を抽出する場合は、次のコマンドを実行します。なお、「-d ,」はファイルの区切り文字(deliminator)を指定するためのオプションで、CSVファイルはカンマが区切り文字なので「,」を指定します。
cut -d , -f 1,4,7,17,26 explosion_20210412.csv
列が少なくなったため、改行が無くなり見やすくなりました。
ファイルに出力する場合は、以下のように実行します。
cut -d , -f 1,4,7,17,26 explosion_20210412.csv > explosion_20210412_v2.csv
4. uniqコマンド ~重複の除去~
説明
実務データにおいて、何らかの理由で同じデータが複数回記録されている場合があります。このようなデータを使用してデータ分析を行う際に、多くの場合、重複を取り除くことが適切です。
uniqコマンドを使うと、ファイルの重複行を取り除くことができます。ただし、uniqコマンドは連続する行が一致している場合にだけ重複を取り除くため、sortコマンドを使って全ての行をキャラクターコード順に並べ替えた後で、uniqコマンドを実行するのが一般的です。
sort ファイル名 | uniq
実行例
3.で実行したコマンド出力結果は全ての行が同じ内容でした。この結果に対して以下のコマンドを実行すると、重複を除くことができます。
sort explosion_20210412_v2.csv | uniq
重複が除かれて、1行だけになりました。ちなみに、この場合は、全ての行が同じ内容だったので、sortをしなくても結果は同じですが、sortとuniqはセットで使うように覚えた方がよいです。
5. sedコマンド ~文字列の置換~
説明
データ分析時のデータの前処理として、文字列の表記ゆれを統一したり、文字列をデータを数値に変換するなど、文字列の置換をすることがよくあります。Excelでも以下のイメージのように、データ内の文字列の置換はできます。
しかし、大きいサイズのデータを扱う場合や、複数の置換パターンがある場合は、Linuxコマンドを使う方が速くて便利です。
sedコマンドを使うと、ファイルの中の特定の文字列を、別の文字列に書き換えることが出来ます。
sed -e 's/返還前の文字列/変換後の文字列/g' ファイル名
複数の置換パターンがある場合は、「|」でつなげて記述します。
sed -e 's/返還前の文字列1/変換後の文字列1/g' ファイル名 | sed -e 's/返還前の文字列2/変換後の文字列2/g'
応用
最後に、今回紹介したコマンドを組み合わせて、以下のデータ処理を一気に行ってみたいと思います。
・対象レコードは、2021年の全種類の出動要請
・対象カラムは、Call Number、Call Type、Received DtTm、City、Call Type Group
・重複は取り除く
・「San Francisco」を「SFO」に書き換える
すでに少し触れていますが、Linuxコマンドは「|」を使うことで、複数の処理をつなげて実行することができます。途中の出力結果を確認する必要がない場合は、「|」を使って一気に実行することで、更に効率化することができます。
grep '/2021,' Fire_Department_Calls_for_Service.csv | cut -d , -f 1,4,7,17,26 | sort | uniq | sed -e 's/San Francisco/SFO/g' > sample_data_2021.csv
PCのスペックにもよりますが、数秒で終わると思います。
出力ファイルを開いたイメージはこちらです。もともと約2GBあったファイルサイズが約3MBまで小さくなっているので、すぐに開けました。
また、ヘッダーを付けたい場合は、以下の様に、まずheadコマンドで1行だけ出力します。その後で、同様の加工処理を実行して、「>」ではなく「>>」を使って追記モードで出力することで、先に出力したヘッダーの後にデータ部分が出力されます。
head -n 1 Fire_Department_Calls_for_Service.csv | cut -d , -f 1,4,7,17,26 > sample_data_2021.csv grep '/2021,' Fire_Department_Calls_for_Service.csv | cut -d , -f 1,4,7,17,26 | sort | uniq | sed -e 's/San Francisco/SFO/g' >> sample_data_2021.csv
こちらも実行は数秒で終わると思います。
同様の処理をExcelやPythonなどのアプリケーションを利用して実行した場合に比べて、Linuxコマンドは圧倒的に速いです。(Linuxコマンド恐るべし!)
Cygwinのインストール|SEを目指す方にオススメのソフトウェア
こんにちは。今回はCygwinのインストール方法ついて紹介します。
Cygwinとは
Cygwinを使うと、Windows PC上にUNIX系OSの実行環境を疑似的に再現することができます。これにより、UNIX系OSの機能をWindowsでも使うことが出来ます。UNIX系OSに興味があるけど環境を用意するのはちょっとハードルが高い、という方にオススメしたいソフトウェアです。
なお、私は前の会社でLinuxサーバ上で動作するソフトウェアの開発を経験していたこともあり、UNIX系OSの代表格であるLinux OSをよく使っていました。また、データを扱う職場に異動してからも、データの加工処理の一部をLinuxのコマンドを使って実行していました。
インストーラのダウンロード
以下のサイトから、インストーラをダウンロードします。
Cygwin Installation
自身の環境に合わせてインストーラを選択します。64ビットOSを使用している場合は、「setup-x86_64.exe」をダウンロードします。最近のPCであれば、64ビットOSだと思いますが、もしわからない場合は、コントロールパネルから辿って確認することができます。
コントロール パネル>システムとセキュリティ>システム
インストール
インストーラのファイルをダブルクリックして実行します。
- Cygwin Net Release Setup Program
- 「次へ」をクリック
- Choose A Download Source
- 「Install from Internet」を選択して、「次へ」をクリック
- Select Root Install Directory
- Cygwinをインストールするフォルダを選択します。例:C:\cygwin64
- Install Forの部分は、特に理由がなければ、「All Users」のままとします。
- 「次へ」をクリック
- Select Local Package Directory
- インストーラの格納フォルダを選択(自動で入力されています。)
- 「次へ」をクリック
- Select Your Internet Connection
- 特に理由がなければ、「Use System Proxy Settings」のままとします。
- 「次へ」をクリック
- Choose A Download Site
- リストの中から、ダウンロードサイトを選択します。アドレスが「.jp」で終わっているものを選ぶと良いです。※リストに何も表示されない場合は、インターネットが接続されていることを確認ください。
- 「次へ」をクリック
- Select Packages
- デフォルトのまま、「次へ」をクリック
- Review and confirm changes
- 「次へ」をクリック
- Progress
- インストール完了を待ちます
- Create Icons
- デスクトップにアイコンを作成する場合は、「Create icon on Desktop」を選択します
- スタートメニューにアイコンを追加する場合は、「Add icon to Start Menu」を選択します
- 「完了」をクリック
これでインストールは完了です。
Cygwinの動作確認
この画面にLinuxのコマンドを入力できます。「pwd」(print working directory)という、現在のフォルダを表示するコマンドを実行してみましょう。
pwd
Cygwinのホームディレクトリ「/home/userpc」が表示されました。userpcの部分は環境によって異なります。
次に、「cd」(change directory)コマンドを使って、PCのCドライブに移動してみます。Cygwinでは、「/cygdrive/c/」が、PCのCドライブを表します。移動後、「ls」(list segment)コマンドを実行して、フォルダ内のファイルやフォルダの一覧を表示します。
cd /cygdrive/c/ ls
このPCのCドライブ直下のファイル・フォルダが見えています。
ここで、Cドライブに移動する時に、毎回「cd /cygdrive/c/」と入力するのは大変なので、「ln」(link)コマンドを実行してホームディレクトリにCドライブへのリンクを作成することをオススメします。
ln -s /cygdrive/c/ ~/c
これで、Cygwinの起動後、ホームディレクトリから「cd c」と入力すればCドライブに移動できます。
cd c