4. 表データとPandasを使いこなそう¶
今回は、データ分析と言えば、避けては通れないPandas の使い方を学び、 表データの操作を練習します。
4.1. Pandas とは¶
Pandas とは、表データを扱うPython モジュールで、表計算ソフト (Excel)やリレーショナルデータベースの操作を Python から手軽に行えるようにしてくれます。
次のようにpandasモジュールをインポートして使います。
[1]:
import pandas as pd
Pandas は、とても高機能です。
本講義では、Pandas でデータ分析をする必須の機能だけ紹介し、ハンズオン演習で体験します。
暗記するものではありません。
どういうデータ処理や操作ができるか、覚えましょう。
使うときは、Google 等で調べながら使ってください。
4.1.1. データの用意¶
Pandas の使い方に慣れるために、小さな練習用のデータを作成して、 そちらを使って演習していきます。
ファイル書き込み機能(%%file)を使って、Colab上でCSVファイル arashi.csvを作ります。
[2]:
%%file arashi.csv
名前,出身,生年,身長,血液型
相葉雅紀,千葉,1982,175,AB
松本潤,東京都,1983,172,A
二宮和也,東京都,1983,168,A
大野智,東京都,1980,166,A
櫻井翔,東京都,1982,171,A
Overwriting arashi.csv
データが間違っている?
一部間違っていると指摘をよく受けます。 どうぞ、各自でご自由に、ご修正して練習にお使いください。
CSV ファイルは、表計算ソフト(Excel)のファイルをデータ処理しやすく、カンマ区切り形式のテキストで出力したものです。当然、Excel 等の表計算ソフトで開いてみることができます。

もうひとつ、あとから表データの結合をする練習で用いる junk.csv (わざと少し壊れたファイル)も作っておきましょう。
[3]:
%%file junk.csv
name,height,weight
大野智,166,52.0
相葉雅紀,176,58.0
二宮和也,168,52.0
松本潤,173,62.0
カビゴン,210,460.0
Overwriting junk.csv
Let’s try
ちゃんとarashi.csvが作成されているか、表計算ソフトで確認してみよう。
4.2. 基本操作¶
Pandas は、表データを効率よく操作できます。
Pandas 用語では、表データのことをデータフレーム(DataFrame)と呼びますが、本資料では表データと呼びます。
覚えておこう:Pandas用語
カラム: 列方向のデータ
インデックス: 行方向のデータ
4.2.1. CSVファイルの読み込み¶
Pandas では、pd.read_csv()を用いると、CSVファイルから表データを読み込めます。
[4]:
data = pd.read_csv('arashi.csv')
data.head() # 最初の5行のみ表示
[4]:
| 名前 | 出身 | 生年 | 身長 | 血液型 | |
|---|---|---|---|---|---|
| 0 | 相葉雅紀 | 千葉 | 1982 | 175 | AB |
| 1 | 松本潤 | 東京都 | 1983 | 172 | A |
| 2 | 二宮和也 | 東京都 | 1983 | 168 | A |
| 3 | 大野智 | 東京都 | 1980 | 166 | A |
| 4 | 櫻井翔 | 東京都 | 1982 | 171 | A |
読み込んで表データは、dataという名前にしてあります。
表データへの操作は、data.head()のようにメソッドで操作します。
4.2.2. 属性名からカラム(列)を取り出す¶
Pandas では、属性ごとのデータ処理が多くなります。 まず、属性名'名前'のデータを取り出してみましょう。
[5]:
data['名前']
[5]:
0 相葉雅紀
1 松本潤
2 二宮和也
3 大野智
4 櫻井翔
Name: 名前, dtype: object
取り出した属性データは、Pythonの列(シーケンス)として、for文などで処理できます。
[6]:
for name in data['名前']:
print(name)
相葉雅紀
松本潤
二宮和也
大野智
櫻井翔
4.2.3. 属性(列データ)の追加¶
新しい属性、つまり列データを追加してみましょう。
表データ中には、5人分のデータがあるため、リストとして5人分のデータを代入すると、 新しい属性を追加することができます。
例. 性別を追加する
[7]:
data['性別'] = ['男性', '男性', '男性', '男性', '男性'] # ['男性'] * 5
data #表示
[7]:
| 名前 | 出身 | 生年 | 身長 | 血液型 | 性別 | |
|---|---|---|---|---|---|---|
| 0 | 相葉雅紀 | 千葉 | 1982 | 175 | AB | 男性 |
| 1 | 松本潤 | 東京都 | 1983 | 172 | A | 男性 |
| 2 | 二宮和也 | 東京都 | 1983 | 168 | A | 男性 |
| 3 | 大野智 | 東京都 | 1980 | 166 | A | 男性 |
| 4 | 櫻井翔 | 東京都 | 1982 | 171 | A | 男性 |
上級者向け: 列に数式を適用する
applyを使えば、各列データに関数やラムダ式を適用した列がえられます。
data['年齢'] = data['生年'].apply(lambda x: 2021 - x)
4.2.4. n行目のデータを取り出す¶
表データの列ごとへのアクセスは、.ilocプロパティを通して行ます。
[8]:
data.iloc[0]
[8]:
名前 相葉雅紀
出身 千葉
生年 1982
身長 175
血液型 AB
性別 男性
Name: 0, dtype: object
[9]:
for values in data.iloc[0]:
print(values)
相葉雅紀
千葉
1982
175
AB
男性
4.2.5. セルの値¶
インデックスと属性の組み合わせで、表データをセルの値を指定して取り出すことができます。
[10]:
data.iloc[0]['出身'] # data['出身'][0]と書いてもよい
[10]:
'千葉'
4.2.6. 表データの出力¶
Pandas で操作した表データは、.to_csv()でCSVファイルに保存できます。
[11]:
data.to_csv('arashi2.csv', index=False) #インデックスなしで出力
[12]:
!cat data.csv
名前,出身,生年,身長,血液型,性別
相葉雅紀,千葉,1982,175,AB,男性
松本潤,東京都,1983,172,A,男性
二宮和也,東京都,1983,168,A,男性
大野智,東京都,1980,166,A,男性
櫻井翔,東京都,1983,171,A,男性
4.3. リレーショナル代数¶
Pandas のリレーショナル代数の操作をみていきましょう。
データベース実習を履修している人は、SQLを思い出しながら試していきましょう。
リレーショナル代数って何?
SQLの元になったデータ操作を集合論をベースに定義した代数系です。 Pandas の操作を覚えるときは、Excel や SQL などの操作と対応付けながら、 マスターしていきましょう。
4.3.1. 選択(selection)¶
選択は、指定した条件に合う行を取り出します。データを抽出するフィルターの役割になります。
例
SELECT * FROM data WHERE '身長 >= 170'
[13]:
data[data['身長'] >= 170]
[13]:
| 名前 | 出身 | 生年 | 身長 | 血液型 | 性別 | |
|---|---|---|---|---|---|---|
| 0 | 相葉雅紀 | 千葉 | 1982 | 175 | AB | 男性 |
| 1 | 松本潤 | 東京都 | 1983 | 172 | A | 男性 |
| 4 | 櫻井翔 | 東京都 | 1982 | 171 | A | 男性 |
複雑な条件は、.query()メソッドを用いて与えることもできます。
[14]:
data.query('身長 >= 170 and 血液型 == "A"')
[14]:
| 名前 | 出身 | 生年 | 身長 | 血液型 | 性別 | |
|---|---|---|---|---|---|---|
| 1 | 松本潤 | 東京都 | 1983 | 172 | A | 男性 |
| 4 | 櫻井翔 | 東京都 | 1982 | 171 | A | 男性 |
4.3.2. 射影(projection)¶
射影(projection)は、表データから属性を限定した表データを返します。 Pandasでは、抽出したい属性名をリストにして渡します。
SQL例
SELECT 名前,生年,血液型 FROM data
[15]:
data[['名前', '生年', '血液型']]
[15]:
| 名前 | 生年 | 血液型 | |
|---|---|---|---|
| 0 | 相葉雅紀 | 1982 | AB |
| 1 | 松本潤 | 1983 | A |
| 2 | 二宮和也 | 1983 | A |
| 3 | 大野智 | 1980 | A |
| 4 | 櫻井翔 | 1982 | A |
4.3.3. 表の連結¶
複数のファイルに入っている表データをまとめてひとつにしたいことがあります。
今度は、最初に作ったもう一つのCSVファイル junk.csvを読み込んで、連結してみます。
[16]:
data2 = pd.read_csv('junk.csv')
data2
[16]:
| name | height | weight | |
|---|---|---|---|
| 0 | 大野智 | 166 | 52.0 |
| 1 | 相葉雅紀 | 176 | 58.0 |
| 2 | 二宮和也 | 168 | 52.0 |
| 3 | 松本潤 | 173 | 62.0 |
| 4 | カビゴン | 210 | 460.0 |
単純に縦方向に連結したいときは、pd.concat()を使って連結します。 (属性名が異なるので、綺麗につながりません)
[17]:
pd.concat([data, data2])
[17]:
| 名前 | 出身 | 生年 | 身長 | 血液型 | 性別 | name | height | weight | |
|---|---|---|---|---|---|---|---|---|---|
| 0 | 相葉雅紀 | 千葉 | 1982.0 | 175.0 | AB | 男性 | NaN | NaN | NaN |
| 1 | 松本潤 | 東京都 | 1983.0 | 172.0 | A | 男性 | NaN | NaN | NaN |
| 2 | 二宮和也 | 東京都 | 1983.0 | 168.0 | A | 男性 | NaN | NaN | NaN |
| 3 | 大野智 | 東京都 | 1980.0 | 166.0 | A | 男性 | NaN | NaN | NaN |
| 4 | 櫻井翔 | 東京都 | 1982.0 | 171.0 | A | 男性 | NaN | NaN | NaN |
| 0 | NaN | NaN | NaN | NaN | NaN | NaN | 大野智 | 166.0 | 52.0 |
| 1 | NaN | NaN | NaN | NaN | NaN | NaN | 相葉雅紀 | 176.0 | 58.0 |
| 2 | NaN | NaN | NaN | NaN | NaN | NaN | 二宮和也 | 168.0 | 52.0 |
| 3 | NaN | NaN | NaN | NaN | NaN | NaN | 松本潤 | 173.0 | 62.0 |
| 4 | NaN | NaN | NaN | NaN | NaN | NaN | カビゴン | 210.0 | 460.0 |
横方向に連結したいときは、axis=1のオプションをつけます。
[18]:
pd.concat([data, data2], axis=1) #横方向に連結
[18]:
| 名前 | 出身 | 生年 | 身長 | 血液型 | 性別 | name | height | weight | |
|---|---|---|---|---|---|---|---|---|---|
| 0 | 相葉雅紀 | 千葉 | 1982 | 175 | AB | 男性 | 大野智 | 166 | 52.0 |
| 1 | 松本潤 | 東京都 | 1983 | 172 | A | 男性 | 相葉雅紀 | 176 | 58.0 |
| 2 | 二宮和也 | 東京都 | 1983 | 168 | A | 男性 | 二宮和也 | 168 | 52.0 |
| 3 | 大野智 | 東京都 | 1980 | 166 | A | 男性 | 松本潤 | 173 | 62.0 |
| 4 | 櫻井翔 | 東京都 | 1982 | 171 | A | 男性 | カビゴン | 210 | 460.0 |
4.3.4. 表データの結合(join)¶
表データの結合は、ふたつの表データのある属性をキーにして、キーが同じ値であれば一つの行にまとめる操作です。
Pandasでは、pd.merge()で結合します。
``名前``と``name``をキーにする
[19]:
pd.merge(data, data2, left_on='名前', right_on='name')
[19]:
| 名前 | 出身 | 生年 | 身長 | 血液型 | 性別 | name | height | weight | |
|---|---|---|---|---|---|---|---|---|---|
| 0 | 相葉雅紀 | 千葉 | 1982 | 175 | AB | 男性 | 相葉雅紀 | 176 | 58.0 |
| 1 | 松本潤 | 東京都 | 1983 | 172 | A | 男性 | 松本潤 | 173 | 62.0 |
| 2 | 二宮和也 | 東京都 | 1983 | 168 | A | 男性 | 二宮和也 | 168 | 52.0 |
| 3 | 大野智 | 東京都 | 1980 | 166 | A | 男性 | 大野智 | 166 | 52.0 |
ふたつの表データは結合されましたが、データが一部消えてしまいました。 これは、Pandas では何も指定しなければ、一番条件の厳しい内部結合が用いられるためです。
結合の方法 * 内部結合 inner: 両方にキーが存在するとき結合 * 外部結合 outer: どちらか一方にキーが存在するとき結合 * 左外部結合 left: 左側にキーが存在するとき * 右外部結合 right: 右側にキーが存在するとき
leftが良さそうですが、outerで結合してみます。
[20]:
pd.merge(data, data2, left_on='名前', right_on='name', how='outer')
[20]:
| 名前 | 出身 | 生年 | 身長 | 血液型 | 性別 | name | height | weight | |
|---|---|---|---|---|---|---|---|---|---|
| 0 | 相葉雅紀 | 千葉 | 1982.0 | 175.0 | AB | 男性 | 相葉雅紀 | 176.0 | 58.0 |
| 1 | 松本潤 | 東京都 | 1983.0 | 172.0 | A | 男性 | 松本潤 | 173.0 | 62.0 |
| 2 | 二宮和也 | 東京都 | 1983.0 | 168.0 | A | 男性 | 二宮和也 | 168.0 | 52.0 |
| 3 | 大野智 | 東京都 | 1980.0 | 166.0 | A | 男性 | 大野智 | 166.0 | 52.0 |
| 4 | 櫻井翔 | 東京都 | 1982.0 | 171.0 | A | 男性 | NaN | NaN | NaN |
| 5 | NaN | NaN | NaN | NaN | NaN | NaN | カビゴン | 210.0 | 460.0 |
また、属性名が英語だったり、日本だったり不統一です。 少し整頓しておきましょう。
[21]:
# data, data2を外部結合した表データを新しく data とする
data = pd.merge(data, data2, left_on='名前', right_on='name', how='outer')
data.drop('name', axis=1, inplace=True)
data.drop('height', axis=1, inplace=True)
data.drop(5, axis=0, inplace=True) #index=5を消す
data.rename(columns={'weight': '体重'}, inplace=True)
data
[21]:
| 名前 | 出身 | 生年 | 身長 | 血液型 | 性別 | 体重 | |
|---|---|---|---|---|---|---|---|
| 0 | 相葉雅紀 | 千葉 | 1982.0 | 175.0 | AB | 男性 | 58.0 |
| 1 | 松本潤 | 東京都 | 1983.0 | 172.0 | A | 男性 | 62.0 |
| 2 | 二宮和也 | 東京都 | 1983.0 | 168.0 | A | 男性 | 52.0 |
| 3 | 大野智 | 東京都 | 1980.0 | 166.0 | A | 男性 | 52.0 |
| 4 | 櫻井翔 | 東京都 | 1982.0 | 171.0 | A | 男性 | NaN |
pandas の表操作は書き換えない
pandas の表操作は、新しい表データを返すようになっています。 だから、別の変数名で別の表データとして操作することもできます。
# 新しい表を data2 として操作する
data2 = data.drop('name', axis=1)
だから、同じ変数名で置き換えることもできます。
data = data.drop('name', axis=1)
データを直接、書き換えたいときは、inplace=Trueをつけます。
data.drop('name', axis=1, inplace=True)
Let’s try
Google で調べて、 dataのカラムの順番を名前 性別 出身 生年 身長 体重 血液型に変更してみよう。
(Google で調べて、操作できるようになれば、OKです。)
4.4. データ分析の準備¶
次回からPandas を用いて、より本格的なデータ分析を始めます。
4.4.1. 欠損値のチェック¶
世の中のデータは、データ値が欠けていることがあります。 今回の練習データでは、NaNと表示されている値が、欠損値になります。 データ件数が少ない場合は、目視でみつかる場合がありますが、 データ件数が多くなると、手作業で探すのは無理です。
欠損値をチェックすることが必要になります。
[22]:
data.isnull().sum()
[22]:
名前 0
出身 0
生年 0
身長 0
血液型 0
性別 0
体重 1
dtype: int64
欠損値が見つかったときは、 欠損値を処理するアプローチとしては:
dropna():データが欠損している行や列を削除するfillna():データが欠損している要素を別の値で穴埋めする
どのように処理するかは、データ分析の目的によって異なります。 今回は、メンバーを消してしまったら、大変なことになりますので、 __欠損値の値を平均値で補完する__ことで対応してみます。
[23]:
data.fillna(data.mean(), inplace=True)
data
[23]:
| 名前 | 出身 | 生年 | 身長 | 血液型 | 性別 | 体重 | |
|---|---|---|---|---|---|---|---|
| 0 | 相葉雅紀 | 千葉 | 1982.0 | 175.0 | AB | 男性 | 58.0 |
| 1 | 松本潤 | 東京都 | 1983.0 | 172.0 | A | 男性 | 62.0 |
| 2 | 二宮和也 | 東京都 | 1983.0 | 168.0 | A | 男性 | 52.0 |
| 3 | 大野智 | 東京都 | 1980.0 | 166.0 | A | 男性 | 52.0 |
| 4 | 櫻井翔 | 東京都 | 1982.0 | 171.0 | A | 男性 | 56.0 |
欠損値の補完
データ分析では、欠損値は何かの嫌がらせかと思うほど、頻繁に生じます。 色々な補完方法がありますので、適切な方法を選んでください。
data['体重'].fillna(50.0) # 体重の欠損値を50.0で穴埋め
data['体重'].fillna(data['体重'].mean()) # 体重の欠損値を体重の平均値で穴埋め
data['体重'].fillna(data['体重'].median()) # 体重の欠損値を体重の中央値で穴埋め
data['体重'].fillna(data['体重'].mode()) # 体重の欠損値を体重の最頻値で穴埋め
4.4.2. グループごとの集計¶
groupby() は、同じ値を持つデータをまとめて、統計処理を行いときに使います。
例. 血液型ごとに平均(mean)をとる
[24]:
data.groupby('血液型').mean()
[24]:
| 生年 | 身長 | 体重 | |
|---|---|---|---|
| 血液型 | |||
| A | 1982.0 | 169.25 | 55.5 |
| AB | 1982.0 | 175.00 | 58.0 |
``describe()``: 基礎統計量を全てみたいとき
[29]:
data.groupby('血液型').describe()
[29]:
| 生年 | 身長 | 体重 | |||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| count | mean | std | min | 25% | 50% | 75% | max | count | mean | ... | 75% | max | count | mean | std | min | 25% | 50% | 75% | max | |
| 血液型 | |||||||||||||||||||||
| A | 4.0 | 1982.0 | 1.414214 | 1980.0 | 1981.5 | 1982.5 | 1983.0 | 1983.0 | 4.0 | 169.25 | ... | 171.25 | 172.0 | 4.0 | 55.5 | 4.725816 | 52.0 | 52.0 | 54.0 | 57.5 | 62.0 |
| AB | 1.0 | 1982.0 | NaN | 1982.0 | 1982.0 | 1982.0 | 1982.0 | 1982.0 | 1.0 | 175.00 | ... | 175.00 | 175.0 | 1.0 | 58.0 | NaN | 58.0 | 58.0 | 58.0 | 58.0 | 58.0 |
2 rows × 24 columns
groupby()は、グループごとに集計して操作するときに、重宝します。 ただし、返されるのはGroupByオブジェクトで表データとして使えません。
ピボットテーブルを用いると、集計結果を表データに変換して取り出せるようになります。
[25]:
pd.pivot_table(data, index="血液型")
[25]:
| 体重 | 生年 | 身長 | |
|---|---|---|---|
| 血液型 | |||
| A | 55.5 | 1982.0 | 169.25 |
| AB | 58.0 | 1982.0 | 175.00 |
ピボットテーブルとクロス集計
ピボットテーブルは、クロス集計に使われる便利ツールです。
index: 縦軸に展開するカラムを指定columns: 横軸に展開するカラムを指定values: 集約する値カラムを指定aggfunc: 集約方法を指定
ぜひ使えるようになりたいところですが、今回のサンプルはデータ件数が少なすぎます。 余力がある人は、次の時系列データで練習してみてください。
4.5. 時系列データ(★)¶
時系列データとは、時間の順番に並んだデータです。 コンピュータが記録するデータ(ログ)は、時系列データになっていることが多く、 データ分析を始める前に理解しやすい形に集計しなおす必要があります。
この節は、Pandas 初心者は、スキップして構いません。
時系列データは、データサイエンスの前処理で重要なデータ形式です。
しかし、初めて Pandas を習ったような場合は、 内容が多すぎて理解が追いつかなくなる可能性があります。 (スキップしてもらって、コースワークに進んでもらって構いません。)
データの入手
UNIXコマンド wgetで以下のURLから取り寄せてください。
!wget https://KuramitsuLab.github.io/data/elearn2018.csv
[30]:
data = pd.read_csv('elearn2018.csv')
data.head()
[30]:
| name | date | problem | point | |
|---|---|---|---|---|
| 0 | Victoria | 2018/5/2 2:35 | 1 | 1 |
| 1 | Victoria | 2018/5/2 2:46 | 2 | 1 |
| 2 | Victoria | 2018/5/2 2:49 | 3 | 1 |
| 3 | Victoria | 2018/5/2 2:53 | 4 | 1 |
| 4 | Victoria | 2018/5/2 3:12 | 5 | 1 |
データの内容は、E-ラーニング教材の学習ログです。
解答者(name)
解答した時間(date)
問題番号(problem)
得点(point)
解答者一覧と回答数
解答者ごとの回答数を調べてみましょう。
[53]:
data['name'].value_counts()
[53]:
Chloe 220
Victoria 164
Zoe 162
Maria 138
Adalynn 90
Name: name, dtype: int64
4.5.1. 日付¶
学習ログの日付は、文字列として記録されています。 このまま処理しても構いませんが、扱いやすいようにpandas の日付データに変換しておきます。
[32]:
data['date'] = pd.to_datetime(data['date'])
data.head()
[32]:
| name | date | problem | point | |
|---|---|---|---|---|
| 0 | Victoria | 2018-05-02 02:35:00 | 1 | 1 |
| 1 | Victoria | 2018-05-02 02:46:00 | 2 | 1 |
| 2 | Victoria | 2018-05-02 02:49:00 | 3 | 1 |
| 3 | Victoria | 2018-05-02 02:53:00 | 4 | 1 |
| 4 | Victoria | 2018-05-02 03:12:00 | 5 | 1 |
すると、日付はメソッド操作で様々な値に変換しやすくなります。
[33]:
print('日付', data['date'][0])
print('年', data['date'][0].year)
print('エポック秒', int(data['date'][0].timestamp()))
日付 2018-05-02 02:35:00
年 2018
エポック秒 1525228500.0
エポック秒とは、UNIX上で使われる1970年1月1日を起点にした秒です。 エポック秒に変換すると、整数値として、日付が処理しやすくなります。 エポック秒に変換し、新しいカラムepochを作っておきましょう。
[34]:
data['epoch'] = data['date'].map(pd.Timestamp.timestamp).astype(int)
data
[34]:
| name | date | problem | point | epoch | |
|---|---|---|---|---|---|
| 0 | Victoria | 2018-05-02 02:35:00 | 1 | 1 | 1525228500 |
| 1 | Victoria | 2018-05-02 02:46:00 | 2 | 1 | 1525229160 |
| 2 | Victoria | 2018-05-02 02:49:00 | 3 | 1 | 1525229340 |
| 3 | Victoria | 2018-05-02 02:53:00 | 4 | 1 | 1525229580 |
| 4 | Victoria | 2018-05-02 03:12:00 | 5 | 1 | 1525230720 |
| ... | ... | ... | ... | ... | ... |
| 769 | Chloe | 2018-09-09 12:01:00 | 187 | 1 | 1536494460 |
| 770 | Chloe | 2018-09-09 12:14:00 | 166 | 1 | 1536495240 |
| 771 | Chloe | 2018-09-17 10:32:00 | 132 | 1 | 1537180320 |
| 772 | Chloe | 2018-09-17 10:35:00 | 133 | 0 | 1537180500 |
| 773 | Chloe | 2018-09-17 10:37:00 | 132 | 1 | 1537180620 |
774 rows × 5 columns
ここから、2018年5月1日を起点に、一週間ごとにデータを区切って、各週ごとの学習状況をみてみたいと思います。
2018年5月1日のエポック秒を調べる
[35]:
import datetime
import time
d = datetime.datetime(2018, 5, 1, 0, 0, 0) # 2018/05/01 のエポック秒
int(time.mktime(d.timetuple()))
[35]:
1525100400
一週間の秒数から、区切りの境界になるエポック秒をリスト化します。
[41]:
epoch_of_week = 7 * 24 * 60 * 60
bins = [1525132800 + i * epoch_of_week for i in range(0, 21)]
print(bins)
[1525132800, 1525737600, 1526342400, 1526947200, 1527552000, 1528156800, 1528761600, 1529366400, 1529971200, 1530576000, 1531180800, 1531785600, 1532390400, 1532995200, 1533600000, 1534204800, 1534809600, 1535414400, 1536019200, 1536624000, 1537228800]
ビン分割(ビニング処理)
連続値を任意の境界値で区切りカテゴリ分けして離散値に変換する処理のこと
Pandasでは、pd.cut()でビン分割します。ラベルは、第1週を1とします。
[43]:
data['week'] = pd.cut(data['epoch'], bins, labels=list(range(1,21))).astype(int)
data
[43]:
| name | date | problem | point | epoch | week | |
|---|---|---|---|---|---|---|
| 769 | Chloe | 2018-09-09 12:01:00 | 187 | 1 | 1536494460 | 19 |
| 770 | Chloe | 2018-09-09 12:14:00 | 166 | 1 | 1536495240 | 19 |
| 771 | Chloe | 2018-09-17 10:32:00 | 132 | 1 | 1537180320 | 20 |
| 772 | Chloe | 2018-09-17 10:35:00 | 133 | 0 | 1537180500 | 20 |
| 773 | Chloe | 2018-09-17 10:37:00 | 132 | 1 | 1537180620 | 20 |
4.5.2. クロス集計¶
週単位でビン分割できたので、各週ごとの集計ができるようになりました。
週ごとの得点集計
[49]:
data.groupby('week')['point'].sum()
[49]:
week
1 147
2 108
3 22
4 25
5 17
6 16
7 28
8 15
9 9
10 12
11 10
12 15
13 8
14 18
15 8
16 28
17 3
19 3
20 2
Name: point, dtype: int64
ピボット表を用いると横軸も指定できます。
各週ごとに各個人の得点集計
[51]:
pd.pivot_table(data, index="week", columns="name", values="point", aggfunc=sum)
[51]:
| name | Adalynn | Chloe | Maria | Victoria | Zoe |
|---|---|---|---|---|---|
| week | |||||
| 1 | 3.0 | 32.0 | 27.0 | 67.0 | 18.0 |
| 2 | 4.0 | 15.0 | 19.0 | 43.0 | 27.0 |
| 3 | 6.0 | 5.0 | 3.0 | 3.0 | 5.0 |
| 4 | 1.0 | 13.0 | 4.0 | 6.0 | 1.0 |
| 5 | 4.0 | 9.0 | 2.0 | NaN | 2.0 |
| 6 | 3.0 | 2.0 | 1.0 | NaN | 10.0 |
| 7 | 3.0 | 11.0 | 3.0 | NaN | 11.0 |
| 8 | 3.0 | 2.0 | 4.0 | NaN | 6.0 |
| 9 | 4.0 | 1.0 | NaN | NaN | 4.0 |
| 10 | 4.0 | 7.0 | NaN | NaN | 1.0 |
| 11 | 4.0 | 1.0 | 3.0 | NaN | 2.0 |
| 12 | 5.0 | 3.0 | 2.0 | 4.0 | 1.0 |
| 13 | 5.0 | 1.0 | NaN | NaN | 2.0 |
| 14 | 15.0 | 0.0 | 0.0 | 0.0 | 3.0 |
| 15 | 4.0 | 4.0 | NaN | NaN | NaN |
| 16 | 1.0 | 10.0 | 7.0 | NaN | 10.0 |
| 17 | NaN | 3.0 | NaN | NaN | NaN |
| 19 | NaN | 3.0 | NaN | NaN | NaN |
| 20 | NaN | 2.0 | NaN | NaN | NaN |
軸を変えてみましょう。 縦軸を個人ごとに変更し、横軸に各問題ごとの解答状況を並べてみます。
[52]:
pd.pivot_table(data, index='name', columns='problem', values='point', aggfunc=max)
[52]:
| problem | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | ... | 122 | 125 | 132 | 133 | 137 | 151 | 162 | 163 | 166 | 187 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| name | |||||||||||||||||||||
| Adalynn | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | ... | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
| Chloe | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | ... | NaN | 1.0 | 1.0 | 0.0 | 0.0 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 |
| Maria | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | ... | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
| Victoria | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | ... | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
| Zoe | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | ... | 1.0 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
5 rows × 117 columns
少し早足でしたが、時系列データを参考にクロス集計を試してみました。 パラメータを変更しながら、どのように変わるかみて、理解を深めてください。
4.6. コースワーク¶
コースワークは、pandasを使ったデータ操作の練習です。
演習(BMI.CSV)
野球選手(B)、サッカー選手(F)、相撲(W)に関するCSVファイルをまとめ、 次の属性からなるCSVファイルにしよう。
名前 身長 体重 職業 BMI
職業は、野球選手B、サッカーF、相撲WとするBMIは身長と体重から計算する身長の高い順にソートして、
bmi.csvとして保存する各職業ごとに、
身長と体重の分布図を描画する各職業ごとにグループ集約し、身長と体重の統計量を求める
データの入手先
鈴木 雅也、渡辺 将人、井上 史斗. 「数式をプログラムするってつまりこういうこと」より、公開データを使わせてもらいます。
データをダウンロードするコマンド
!wget https://raw.githubusercontent.com/massongit/math-program-book/master/9_data/サッカー/Jリーグ選手身長体重.csv
!wget https://raw.githubusercontent.com/massongit/math-program-book/master/9_data/プロ野球/プロ野球選手身長体重.csv
!wget https://raw.githubusercontent.com/massongit/math-program-book/master/9_data/相撲/力士身長体重.csv