ペンギンと南極でデータ分析練習①
はじめに
みなさんデータ分析の勉強するときにどんなデータを最初に触りましたか?
よくチュートリアルとして使われるのは、Kaggleのタイタニックであったり、ほかにもirisのアヤメだったりを使ってるものがひじょーに多いですよね。
そんなirisデータセットになり替わることを目標(?)に掲げている、palmerpenguinsというペンギンのデータセットというものを見つけたので触っていきます。
irisで花弁いじいじするよりもペンギンのほうが可愛いです。間違いない。
palmerpenguinsについて
いくつかの種類のペンギンの性別やサイズなどのデータが集められたデータセットになってます。
データのもととなっている研究では餌の量と性別比の関係性?の生物学的な理論があるらしく、それに関してペンギンで調査をしたようです。
いくつかアートワークも載せられていてとても愛らしいです。
可愛い。とてもかわいい。すごくかわいい。
実行環境
Google Colaboratory
データの分析
さて、実際にデータの分析を行ってみましょう。まずはGitHubからデータをダウンロードします。
#data dowmload
! git clone https://github.com/allisonhorst/palmerpenguins.git
#path rawdata_path = "./palmerpenguins/inst/extdata/penguins_raw.csv" data_path = "./palmerpenguins/inst/extdata/penguins.csv"
!pip install plotly
#import module import os import pandas as pd import numpy as np import plotly.graph_objects as go from plotly.offline import init_notebook_mode, iplot
#reading the data
rawpenguins_df = pd.read_csv(rawdata_path)
penguins_df = pd.read_csv(data_path)
display(rawpenguins_df.head())
display(penguins_df.head())
studyName | Sample Number | Species | Region | Island | Stage | Individual ID | Clutch Completion | Date Egg | Culmen Length (mm) | Culmen Depth (mm) | Flipper Length (mm) | Body Mass (g) | Sex | Delta 15 N (o/oo) | Delta 13 C (o/oo) | Comments | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | PAL0708 | 1 | Adelie Penguin (Pygoscelis adeliae) | Anvers | Torgersen | Adult, 1 Egg Stage | N1A1 | Yes | 2007-11-11 | 39.1 | 18.7 | 181.0 | 3750.0 | MALE | NaN | NaN | Not enough blood for isotopes. |
1 | PAL0708 | 2 | Adelie Penguin (Pygoscelis adeliae) | Anvers | Torgersen | Adult, 1 Egg Stage | N1A2 | Yes | 2007-11-11 | 39.5 | 17.4 | 186.0 | 3800.0 | FEMALE | 8.94956 | -24.69454 | NaN |
2 | PAL0708 | 3 | Adelie Penguin (Pygoscelis adeliae) | Anvers | Torgersen | Adult, 1 Egg Stage | N2A1 | Yes | 2007-11-16 | 40.3 | 18.0 | 195.0 | 3250.0 | FEMALE | 8.36821 | -25.33302 | NaN |
3 | PAL0708 | 4 | Adelie Penguin (Pygoscelis adeliae) | Anvers | Torgersen | Adult, 1 Egg Stage | N2A2 | Yes | 2007-11-16 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | Adult not sampled. |
4 | PAL0708 | 5 | Adelie Penguin (Pygoscelis adeliae) | Anvers | Torgersen | Adult, 1 Egg Stage | N3A1 | Yes | 2007-11-16 | 36.7 | 19.3 | 193.0 | 3450.0 | FEMALE | 8.76651 | -25.32426 | NaN |
species | island | bill_length_mm | bill_depth_mm | flipper_length_mm | body_mass_g | sex | year | |
---|---|---|---|---|---|---|---|---|
0 | Adelie | Torgersen | 39.1 | 18.7 | 181.0 | 3750.0 | male | 2007 |
1 | Adelie | Torgersen | 39.5 | 17.4 | 186.0 | 3800.0 | female | 2007 |
2 | Adelie | Torgersen | 40.3 | 18.0 | 195.0 | 3250.0 | female | 2007 |
3 | Adelie | Torgersen | NaN | NaN | NaN | NaN | NaN | 2007 |
4 | Adelie | Torgersen | 36.7 | 19.3 | 193.0 | 3450.0 | female | 2007 |
csvファイルとして生データのpenguins_raw.csvと、多少整形されたpenguins.csvが用意されてます。今回はpenguins.csvを使っていきます。
penguins_df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 344 entries, 0 to 343
Data columns (total 8 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 species 344 non-null object
1 island 344 non-null object
2 bill_length_mm 342 non-null float64
3 bill_depth_mm 342 non-null float64
4 flipper_length_mm 342 non-null float64
5 body_mass_g 342 non-null float64
6 sex 333 non-null object
7 year 344 non-null int64
dtypes: float64(4), int64(1), object(3)
memory usage: 21.6+ KB
344匹のペンギンのデータです。
penguins_df.isnull().sum()
species 0
island 0
bill_length_mm 2
bill_depth_mm 2
flipper_length_mm 2
body_mass_g 2
sex 11
year 0
dtype: int64
どうやらデータには欠損値が含まれてるようですね。
欠損値が含まれてる場合には、
①欠損値が含まれ列or行の削除
②別の数値で穴埋めする
などのアプローチがあります。
...が、今回はデータ数が344と少なめなため、欠損値処理で平均値をとっても正確でない可能性があります。
なのでずっぱしと削除してしまいます。
penguins_df = penguins_df.dropna()
さて、欠損値を削除したので、ここからデータを見ていきましょう
penguins_df.describe()
bill_length_mm | bill_depth_mm | flipper_length_mm | body_mass_g | year | |
---|---|---|---|---|---|
count | 333.000000 | 333.000000 | 333.000000 | 333.000000 | 333.000000 |
mean | 43.992793 | 17.164865 | 200.966967 | 4207.057057 | 2008.042042 |
std | 5.468668 | 1.969235 | 14.015765 | 805.215802 | 0.812944 |
min | 32.100000 | 13.100000 | 172.000000 | 2700.000000 | 2007.000000 |
25% | 39.500000 | 15.600000 | 190.000000 | 3550.000000 | 2007.000000 |
50% | 44.500000 | 17.300000 | 197.000000 | 4050.000000 | 2008.000000 |
75% | 48.600000 | 18.700000 | 213.000000 | 4775.000000 | 2009.000000 |
max | 59.600000 | 21.500000 | 231.000000 | 6300.000000 | 2009.000000 |
統計量はこんな感じですね。
まずは島ごとにどのペンギンが住んでるか見てみましょう
pd.DataFrame(penguins_df.groupby(["island","species"])["species"].count())
species | ||
---|---|---|
island | species | |
Biscoe | Adelie | 44 |
Gentoo | 119 | |
Dream | Adelie | 55 |
Chinstrap | 68 | |
Torgersen | Adelie | 47 |
提供されてるデータには3つの島があり、アデリーペンギンだけはどの島にも住んでいるようです。
この島は南極大陸のどこにあるのかもfoliumを使って可視化してみます。
!pip install folium
import folium biscoe = [-65.433333,-65.5] dream = [-64.733333,-64.233333] torgersen = [-64.766667,-64.083333] LAT = (biscoe[0]+dream[0]+torgersen[0])/3 LNG = (biscoe[1]+dream[1]+torgersen[1])/3 m = folium.Map(location=[-64.766667,-64.083333],zoom_start=7) folium.CircleMarker( radius = 10, location = biscoe, popup = "<b>" + "biscoe" + "</b>", color = "#7fffd4", fill_color= "#7fffd4", fill = True, ).add_to(m) folium.CircleMarker( radius = 10, location = dream, popup = "<b>" + "dream" + "</b>", color = "#ffd700", fill_color= "#ffd700", fill = True, ).add_to(m) folium.CircleMarker( radius = 10, location = torgersen, popup = "<b>" + "torgersen" + "</b>", color = "#ff7f50", fill_color= "#ff7f50", fill = True, ).add_to(m) m
See the Pen zYqpEZM by yamatia (@yamatia) on CodePen.
jupyter book上ではもう少しスタイリッシュな形式で見れるのですが、 はてなブログに張り付ける都合上、CodePenを使ってhtmlを張り付けています。
地形的にはBiscoeだけ海を挟んだ先のようですね。
まあ正直なところ南極の地図見てもどこがどこだかさっぱりです。北極と差し替えられてても気づかないでしょう...
次に3種のペンギンはどのような違いがあるのでしょうか。GitHubにアートワークがあげられてました。
試しに画像を表示してみましょう。
from IPython.display import Image,display_png display_png(Image("./palmerpenguins/man/figures/lter_penguins.png",height=400,width=800))
可愛い。もう可愛い。たまらんですね...
提供されてるデータはどのようなものでしょうか?
penguins_df.columns
Index(['species', 'island', 'bill_length_mm', 'bill_depth_mm',
'flipper_length_mm', 'body_mass_g', 'sex', 'year'],
dtype='object')
display_png(Image("./palmerpenguins/man/figures/culmen_depth.png",height=600,width=800))
くちばし回りは画像の部分ですね。
他には、speciesはペンギンの種類、islandは島の名前、sexはオスかメスか、flipper_lengthはつばさの長さ、body_mass_gは体重ですね。
yearについては...データ元の論文読みましたがどうやら卵1つの段階(one-egg stage)で生体捕獲してサイズなど測定しているのでその記録日でしょうか?
さて、ここから本格的にデータを見ていきましょう。
penguins_df.head()
species | island | bill_length_mm | bill_depth_mm | flipper_length_mm | body_mass_g | sex | year | |
---|---|---|---|---|---|---|---|---|
0 | Adelie | Torgersen | 39.1 | 18.7 | 181.0 | 3750.0 | male | 2007 |
1 | Adelie | Torgersen | 39.5 | 17.4 | 186.0 | 3800.0 | female | 2007 |
2 | Adelie | Torgersen | 40.3 | 18.0 | 195.0 | 3250.0 | female | 2007 |
4 | Adelie | Torgersen | 36.7 | 19.3 | 193.0 | 3450.0 | female | 2007 |
5 | Adelie | Torgersen | 39.3 | 20.6 | 190.0 | 3650.0 | male | 2007 |
まずは種類や島ごとの個体数、性別について見ていきます。
count_df = pd.DataFrame(penguins_df.groupby(["species","island","sex"])["species"].count()).rename(columns={"species":"counts"}) count_df
counts | |||
---|---|---|---|
species | island | sex | |
Adelie | Biscoe | female | 22 |
male | 22 | ||
Dream | female | 27 | |
male | 28 | ||
Torgersen | female | 24 | |
male | 23 | ||
Chinstrap | Dream | female | 34 |
male | 34 | ||
Gentoo | Biscoe | female | 58 |
male | 61 |
import plotly.express as px
fig = px.bar(count_df.reset_index(),x="sex",y="counts",color="sex",barmode="group",facet_row="island",facet_col="species", category_orders={"species":["Adelie","Chinstrap","Gentoo"],"island":["Biscoe","Dream","Torgersen"],}) fig.show()
アデリーペンギンの島ごとの個体数や、それぞれ性差での個体数は大きな違いは見られませんね。
次はサイズについて見ていきましょう。
島ごとにペンギンの生育環境が大きく違っていると、今回のように島ごとに種類が違うデータを比較するときに不都合が生まれる可能性もあります、
なのでまずは、 3つの島のすべてに生息しているアデリーペンギンのデータを比較してみてみます。
import plotly.figure_factory as ff from plotly.subplots import make_subplots import plotly.graph_objects as go adelie_df = penguins_df[penguins_df["species"]=="Adelie"] features = ["bill_length_mm","bill_depth_mm","flipper_length_mm","body_mass_g"] for feature in features: df1 = adelie_df[adelie_df["island"]=="Biscoe"][feature] df2 = adelie_df[adelie_df["island"]=="Dream"][feature] df3 = adelie_df[adelie_df["island"]=="Torgersen"][feature] hist_data = [df1,df2,df3] group_labels = ["Biscoe","Dream","Torgersen"] fig = ff.create_distplot(hist_data,group_labels, histnorm= 'probability', bin_size=adelie_df[feature].max()/100,curve_type="kde") fig.update_layout(title=feature,bargap=0.01) fig.show()
あまり顕著な差は見られませんね...地形的にもちょっと海を挟んだだけなので大きな差はないのかもしれません。
島ごとにあまり差はなさそうなことが分かったので、次は島の差は無視して種類ごとのサイズの違いを見ていきましょう。
import plotly.express as px fig = px.scatter_matrix(penguins_df, dimensions=["bill_length_mm","bill_depth_mm","flipper_length_mm","body_mass_g"], color="species", width=800,height=800) fig.show()
わりと種類ごとのサイズには違いがあるようですね。
例えば、ジェンツーペンギンなどはbill_depth_lengthが小さいものが多いですね。
他にも、bill_depth_mmとbill_length_mmのプロットを見ればかなりはっきり分離しているので、くちばしのサイズデータがあればペンギンの種類が見分けられそう...と考えられますね。
こんな風に特徴量を見てると、機械学習でサイズデータからペンギンの種類を予測できそうかどうか...といった予想や方針を考えられると思います。
さらに詳しく見ていきます。
features = ["bill_length_mm","bill_depth_mm","flipper_length_mm","body_mass_g"] for feature in features: df1 = penguins_df[penguins_df["species"]=="Adelie"][feature] df2 = penguins_df[penguins_df["species"]=="Gentoo"][feature] df3 = penguins_df[penguins_df["species"]=="Chinstrap"][feature] hist_data = [df1,df2,df3] group_labels = ["Adelie","Gentoo","Chinstrap"] fig = ff.create_distplot(hist_data,group_labels, histnorm= 'probability', bin_size=penguins_df[feature].max()/100,curve_type="kde") fig.update_layout(title=feature,bargap=0.01) fig.show()
割とはっきりと分布の違いが見て取れますね。ジェンツーペンギンは羽は長めで重いことが多いようです。ぽっちゃり系ですね。
くちばしの長さと深さの2軸でデータを見てみます。
import plotly.express as px fig = px.scatter(penguins_df, x="bill_length_mm", y="bill_depth_mm", color="species", marginal_y="violin", marginal_x="box", trendline="ols", template="simple_white") fig.show()
かなりくっきり分かれてて気持ちがいいですね。
ペンギン博士となれば目隠ししててもくちばしを触るだけで種類がわかるのかもしれません。
最後に特徴量どうしの相関について見てみましょう
import seaborn as sns import matplotlib.pyplot as plt fig = plt.figure(figsize=(15,15),facecolor="white") sns.heatmap(pd.get_dummies(penguins_df).corr(),cmap="coolwarm")
まあデータの分布をみて直感的に感じることと一致します。例えば、Gentooとflipper_length_mmは高い相関となっています。
まとめ
さて、こんな感じでペンギンのデータを使って可視化をしてみました。
pandasやplotlyのいい練習になりますね。
それだけではありません。データに触れてるだけでペンギンへの信仰心が高まるという副次効果もあります。
植物よりは動物のほうが強いに決まってるので(?)、皆さんもぜひペンギンのデータをいじってみてください。
データ数が少ないので微妙ですが、またいつかこちらのデータを使って機械学習の練習をしてみる予定です。
参考記事
Members. data #2 データ可視化・ライブラリLT編 参加記録
はじめに
2020/7/31開催のLTイベントに参加してきたのでその参加録です。 このブログ参加記録しか書いてないけど大丈夫ですか?LTの内容もしっかりブログに書き起こして、どうぞ。
イベント概要
@yasshi_dayooonさん主催のイベントで、今回はデータ可視化・ライブラリ、ツールに関することがテーマでした。
1.Bリーグデータから学ぶ決定木分析結果の可視化(graphviz/dtreeviz)@light940
決定木の可視化について、graphviz,dtreevizの説明など。 こういったのは第三者に(特に機械学習を知らない人)へ説明するときに便利ですね。
自分も研究で教授とポケモンバトル(隠語)する時に多用した手法です。 具体例としてBリーグデータ使ってたのは興味深かった、失点が74.5点以上か以下で勝敗の分類が決まる...?
2.Dashハンズオン@OgawaHideyuki
こちらはLTではなくDashのハンズオン。colabが用意されてたのが良き。真似したい。
Dashはちょいちょい勉強中でstreamlitに逃げてしまうのでマスターしたいですね。JupyterDashなるものをはじめて知った。
3.PythonでInteractive Network Graph Visualization!
まあ自分ですね。network graphをinteractiveに可視化する旅をしてたのでその話をしました。 結論はnetworkx+pyvisがお手軽便利。一周回って特に見つからない旅でした(落胆)
Atcoderのグラフ問題可視化はもっと本腰入れて完成させたい。。 しかし精進しないとレートが伸びない...(苦悩)
まとめ
今回もLTしてきました。いつまでたってもオンラインLTは慣れない。壁に向かって小粋なジョーク飛ばせる人メンタル強すぎない...?吉本の擬人化か?
前回は論文LTで重かったので今回は軽くやってみた系にしたら羽毛のような軽さで終わってしまった。LTの量と密度のベストポジションを見つけたい。。。
Members.data #1 機械学習LT編 [オンライン] 参加記録
はじめに
6/26にオンラインで開催されたMembers.data #1 機械学習LT編 に参加してきました! その参加記録です...
全部で5人!10分のショートLTだったのでサクサク進んだ印象。
LT1:Hidehisa Araiさん
最近はやりのstreamlitについての話。デモがメインで見やすかった...
最近触り始めたのであー...って感じでずっと聞いてました。
まあうすうす感じてはいましたが、やっぱり配置であったり、I/Oには課題がまだあるようで。Dashとかと使い分けられるようになるのが一番なんでしょうねー...
LT2:masakazu_kawazuさん
BERTで文章の関連度算出してみるおはなし。
「くまモンvsふなっしー」だったり、「あいみょんvs西野カナ」だったりでcos類似度出してみてるサンプルが面白かった。
BERTのモデルはやっぱり重いよねとのこと。軽量化は研究が進んで日本語版で出るのがいいですよね~。
LT3:杏仁まぜそばさん
マイナーだけど対処が必要な機械学習の問題があるよねというお話。
"思いつく問題のリスト"を挙げていて、不均衡データであったり、データ本当にあってるかもんだいだったり...
気を付けないとすべて破綻しますねリストで勉強になった。
情報発信はどんどんされたほうがいいよなーと。
LT4:自分
まあ自分の登壇LTですね。
他の人と違って実務の豆知識だったりやってみた系ではないので、ひたすらに表情認識の論文とタスクを紹介する
研究室報告会ですかここは?という発表だった。
趣味で調べたことアウトプットしたかったんだ...許しておくれ
LT5:Goda kantoさん
fastTextをつかって極性辞書を生成してみたよ!という話。
Qiita記事も拝読したことあったのでなるほどなーという内容。
そもそもフリー利用可能な日本語極性辞書が少ないんだよな... 自分の研究だとドメインの影響ありすぎてうまくいかなかった思い出がが...
まとめ
実はオンラインLTは初めてなので登壇中は終始まごまごしてました。まず相手に繋がってるのかが不安すぎる...
10分間何も聞こえてませんでした。とかだとギャグ展開すぐるからなー。なんかよいライフハックはないものか。
会自体はMLで知ってること、知らないこと、人の発表を聞けるのはやっぱり新鮮ですね。とくにやってみた系は楽しく聞けるのでLT向けなのかなーと
ブログはじめました
自己紹介
はじめまして。エビティアといいます。 医療系の学部で学生やっています。
研究室で機械学習に触れて、今は医療文書にたいして自然言語処理ごにょごにょするような内容を扱ってます。
Qiitaとかにはたまに投稿してますが、そこに投稿できないポエミーなことや技術的な取り組みなどを載せていけたらと思います。
最近のあれこれ
最近はAtcoder、Kaggleに触れて「うわ...私の技術力、低すぎ...?」と心ポッキーになる日々を過ごしてます。力が足りないよ、力が!
まあ原因は明らかですね、大学で特に専攻していなかったのと、根本的な数学力が欠如してます。
あとは一応医療系学部なので来年には国家試験があります。あるよね...?あるはずです(多分)。 コロナの影響でどうなるかわかりませんが、消滅してしまったらその時はその時です。
うちの学校も休校になり、授業はどこかへ消えてなくなってしまいました。今は高い学費が虚無に葬られるさまを眺めるだけの謎の機関に所属している...
これからの目標
技術力アップとモチベーション維持のためにも当面の目標は週一ペースで記事を投稿することをめどにやっていけたらと思います。