前回のPandasのきほんのきに引き続き、Dataquestの問題を解いていこうと思います。取り扱うデータは前回の投稿のものと同じです。
#データの取り扱いについて
##データ処理(計算)
pandasでは列を指定してするだけでその列内のデータ全てを書き換えてくれる。ただ(自分の解釈が正しければ)これは既存のデータを書き換えているわけではなく新しく更新されたデータを生成している。
div_1000 = food_info["Iron_(mg)"] / 1000
# mg -> gに変えたいとき
# Adds 100 to each value in the column and returns a Series object.
add_100 = food_info["Iron_(mg)"] + 100
# Subtracts 100 from each value in the column and returns a Series object.
sub_100 = food_info["Iron_(mg)"] - 100
# Multiplies each value in the column by 2 and returns a Series object.
mult_2 = food_info["Iron_(mg)"]*2
データ同士での処理もコラム別にまとめて行うことができる。
water_energy = food_info["Water_(g)"] * food_info["Energ_Kcal"]
![Screen Shot 2018-03-15 at 8.43.33 PM.png](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.amazonaws.com%2F0%2F87666%2Fb87d198c-19c5-c418-2156-5a99cf573f17.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&s=a676288793f8a09ca09330a8e4ffe51e)
##数字の単位を揃えてたい
今回のテーブル内のデータの数値の統合性を図るために以下の式を用いてデータの値の範囲を0から1の間にする。
x' = \frac{x-min(x)}{max(x) - min(x)}
例:
# The largest value in the "Energ_Kcal" column.
max_calories = food_info["Energ_Kcal"].max()
# Divide the values in "Energ_Kcal" by the largest value.
normalized_calories = food_info["Energ_Kcal"] / max_calories
##計算した列丸ごと新しい列として追加する
基本的な考え方はpythonのdictionaryと同じ。
iron_grams = food_info["Iron_(mg)"] / 1000
food_info["Iron_(g)"] = iron_grams
##データの表示される優先度を変える
例えばSodium(g)
列に表示されるデータの優先順位を変えるには
food_info.sort_values("Sodium_(mg)")
Pandasはデフォルトで新しいdataframeを作成してしまうのでinplace
という引数を使って新たにdataframeを作らないようにする。また表示される順番もascending=True
だからと言ってdescending=True
にすればいいというわけではないので注意。代わりにascending=False
とする。
# Sorts the DataFrame in-place, rather than returning a new DataFrame.
food_info.sort_values("Sodium_(mg)", inplace=True)
# Sorts by descending order, rather than ascending.
food_info.sort_values("Sodium_(mg)", inplace=True, ascending=False)
ちなみにDataframe.iloc[]
で表示したいデータの数を操作できる。iloc
は_integer location_の略。
first_five_rows = new_titanic_survival.iloc[0:5]
##Indexing
loc
とiloc
を使うのが主流のよう。.loc[]
では取り出したいコラムの名前をストリング/数字で指定。iloc[]
では数字指定。
first_row_first_column = new_titanic_survival.iloc[0,0]
all_rows_first_three_columns = new_titanic_survival.iloc[:,0:3]
row_index_83_age = new_titanic_survival.loc[83,"age"]
row_index_766_pclass = new_titanic_survival.loc[766,"pclass"]
###ReIndexing
データをいじっていると時としてもう一度インデックスし直した方が都合がいい場合がある。データ取得に弊害が生じる可能性があるからだ。そんな時はDataframe.reset_index()
で実行可能。また、古いデータのインデックスを一度取り除いたい場合はdrop=True
にすることを忘れずに。
#Reindex the new_titanic_survival dataframe so the row indexes start from 0, and the old index is dropped.
#Assign the final result to titanic_reindexed.
#Print the first 5 rows and the first 3 columns of titanic_reindexed.
titanic_reindexed = new_titanic_survival.reset_index(drop=True)
print(titanic_reindexed.iloc[0:5, 0:3])
##NaN (null)への対処法
pandas.isnull()
でチェックできる。例えばちょっとした応用としてsex
がnull
の行だけを取得したい場合は
# sex列を取得
sex = titanic_survival["sex"]
# nullの値がある値判定、True/Falseベースで生成
sex_is_null = pandas.isnull(sex)
# Trueとなった行だけを取得
sex_null_true = sex[sex_is_null]
Dataframe.dropna()
というメソッドを使えばマトリックスに対して値がNaNの行を排除してくれる。
The dropna() method takes an axis parameter, which indicates whether you would like to drop rows or columns. Specifying axis=0 or axis='index' will drop any rows that have null values, while specifying axis=1 or axis='columns' will drop any columns that have null values. We will use 0 and 1 since they're more commonly used, but you can use either.
dropna()
というメソッドはどうやらaxis
という引数を持っているらしい。これはつまるところ「排除したいものはNaN
値が含まれている行なのかそれとも列なのか」ということである。行を取り除きたい場合は0
もしくはindex
を使い、列を取り除きたい場合は1
かcolumns
を使う。
drop_na_rows = titanic_survival.dropna(axis=0)
##Pivot Table
一つのデータを多角的な視点から集計、比較する際に便利なピボットテーブルの使い方など。例題3のように既存メソッドを使わずにマニュアルでもできるが今回はDataframe.pivot_table()
を使って問題を解いていく。
passenger_class_fares = titanic_survival.pivot_table(index="pclass", values="fare", aggfunc=np.mean)
-
index
でグルーピングしたいコラムの指定 -
values
で計算やメソッドをあてたいコラムの指定 -
aggfunc
であてたいメソッドの種類を指定する
#メソッドの適用
dataframeに対してある特定のメソッドを適用させたい場合はDataframe.apply()
を使う。個人的にはfor
ループをイメージしている。
# This function returns the hundredth item from a series
def hundredth_row(column):
# Extract the hundredth item
hundredth_item = column.iloc[99]
return hundredth_item
# Return the hundredth item from each column
hundredth_row_var = titanic_survival.apply(hundredth_row)
引数にaxis=1
を入れてあげれば列ではなく行に対してメソッドを適用させることもできる。
def which_class(row):
pclass = row['pclass']
if pd.isnull(pclass):
return "Unknown"
elif pclass == 1:
return "First Class"
elif pclass == 2:
return "Second Class"
else:
return "Third Class"
classes = titanic_survivors.apply(which_class, axis=1)
#例題集
自分が気になった問題や新たな発見につながった問題をピックアップして備忘録としてここに残しておこうと思います。
##例題1
- Count how many values in the "age" column have null values:
- Use
pandas.isnull()
on age variable to create a Series ofTrue
andFalse
values. - Use the resulting series to select only the elements in age that are null, and - assign the result to
age_null_true
- Assign the length of
age_null_true
toage_null_count
. - Print
age_null_count
to see how many null values are in the "age" column.
##自分の解答
age = titanic_survival["age"]
age_is_null = age.isnull()
age_null_true = age[age_is_null]
age_null_count = age_null_true.shape[0]
print(age_null_count)
##例題2
- Use age_is_null to create a vector that only contains values from the "age" column that aren't NaN.
- Calculate the mean of the new vector, and assign the result to correct_mean_age.
##自分の解答
age_is_null = pd.isnull(titanic_survival["age"]) == False
age_null_false = titanic_survival[age_is_null]
correct_mean_age = sum(age_null_false["age"]) / len(age_null_false)
##反省
要所要所でどういうデータをとってきているのかのイメージを描けていない。例えば今回の問題では当初以下のようなコードでしばらくの間エラーが続いた。
age_is_null = pd.isnull(titanic_survival["age"]) == False
age_null_false = titanic_survival[age_is_null]
correct_mean_age = sum(age_null_false) / len(age_null_false)
ここではage=nullではない列の値の合計値が欲しいのでまずは__age_null_false
というテーブルの中のage
コラムをとってくる必要がある__。これをせずにテーブルをそのままぶん投げていたのでエラーが起きていた。
追記:
似たようなエラーにまた遭遇したので備忘録としてメモしておく。
import pandas as pd
data = pd.read_csv("thanksgiving.csv", encoding="Latin-1")
dont_celebrate = pd.isnull(data["Do you celebrate Thanksgiving?"]) == False
celebrate_only = data[dont_celebrate]
celebrate_only.value_counts()
#AttributeError: 'DataFrame' object has no attribute 'value_counts'
celebrate_only
はあくまで2次元のdataframeなのでcelebrate_only["column"]
と指定して一次元のデータに変えてあげないとSeriesメソッドは使えない。ちなみに指定したコラム内の値をカテゴリ別採算を出したい場合はpandas.Series.value_counts()
がお勧め。
import numpy as np
typical_main_dish = pd.isnull(data["What is typically the main dish at your Thanksgiving dinner?"]) == False
new_data = data[typical_main_dish]
new_data["What is typically the main dish at your Thanksgiving dinner?"].value_counts()
###対処法2
pandasのメソッドの多くは自動的にNaN
値を弾いてくれるらしい。
correct_mean_fare = titanic_survival["fare"].mean()
##例題3
- Use a
for
loop to iterate overpassenger_classes
. Within thefor
loop: - Select just the rows in
titanic_survival
where thepclass
value is equivalent to the current iterator value (class). - Select just the
fare
column for the current subset of rows. - Use the
Series.mean
method to calculate the mean of this subset. - Add the mean of the class to the
fares_by_class
dictionary with class as the key. - Once the loop completes, the dictionary fares_by_class should have 1, 2, and 3 as keys, with the average fares as the corresponding values.
passenger_classes = [1, 2, 3]
fares_by_class = {}
for passenger_class in passenger_classes:
pclass_rows = titanic_survival[titanic_survival["pclass"] == passenger_class]
pclass_fares = pclass_rows["fare"]
mean_fare = pclass_fares.mean()
fares_by_class[passenger_class] = mean_fare
pclass_rows_equivalent = pandas.isnull(titanic_survival["pclass"] == passenger_class)
pclass_rows = titanic_survival[pclass_rows_equivalent]
上記のisnull
はどうやらブーリアンのみ適用可っぽい
pclass_rows = titanic_survival[titanic_survival["pclass"] == passenger_class]
##例題4
- Use the
DataFrame.pivot_table()
method to calculate the mean age for each passenger class ("pclass"
). - Assign the result to
passenger_age
. - Display the
passenger_age
pivot table using theprint()
function.
import numpy as np
passenger_age = titanic_survival.pivot_table(index="pclass", values="age", aggfunc=np.mean)
print(passenger_age)
##例題5
values
に自分の求めたいカラムをリストにしまって引数として渡してあげると複数のカラムに対して処理を行うことができる。例えば以下の例題ではembarked
別にグルーピングされたデータに対してカラム別にfare
とsurvived
の値の合計を取得している。
- Make a pivot table that calculates the total fares collected (
"fare"
) and total number of survivors ("survived"
) for each embarkation port ("embarked"
). - Assign the result to port_stats.
- Display
port_stats
using theprint()
function.
import numpy as np
port_stats = titanic_survival.pivot_table(index="embarked", values=["fare", "survived"], aggfunc=np.sum)
print(port_stats)
##例題6
Drop all rows in titanic_survival
where the columns "age"
or "sex"
have missing values and assign the result to new_titanic_survival
.
new_titanic_survival = titanic_survival.dropna(axis=0, subset=["age", "sex"])
##例題7
- Assign the first ten rows from new_titanic_survival to first_ten_rows.
- Assign the fifth row from new_titanic_survival to row_position_fifth.
- Assign the row with index label 25 from new_titanic_survivalto row_index_25.
###自分の解答
# We have already sorted new_titanic_survival by age
first_ten_rows = new_titanic_survival.iloc[0:10]
row_position_fifth = new_titanic_survival.iloc[4]
row_index_25 = new_titanic_survival.iloc[24]
###反省点
Remember to use .loc when addressing by label, and .iloc when indexing by position.
.loc
はインデックス(unique id)別に検索したい時に使い.iloc
はデータの物理的な立ち位置(ポジション)を指定したい時に使う。
# We have already sorted new_titanic_survival by age
first_ten_rows = new_titanic_survival.iloc[0:10]
row_position_fifth = new_titanic_survival.iloc[4]
row_index_25 = new_titanic_survival.loc[25]
##例題8
- Assign the value at row index label
1100
, column index label"age"
fromnew_titanic_survival
torow_index_1100_age
. - Assign the value at row index label
25
, column index label"survived"
from new_titanic_survival
torow_index_25_survived
. - Assign the first 5 rows and first three columns from
new_titanic_survival
tofive_rows_three_cols
.
row_index_1100_age = new_titanic_survival.loc[1100, "age"]
row_index_25_survived = new_titanic_survival.loc[25, "survived"]
five_rows_three_cols = new_titanic_survival.iloc[0:5, 0:3]
##例題9
- Write a function that counts the number of
null
elements in a Series. - Use the
DataFrame.apply()
method along with your function to run across all the columns intitanic_survival
. - Assign the result to
column_null_count
.
def null_count(column):
is_null = column.isnull()
null_columns = titanic_survival[is_null]
return null_columns.shape[0]
column_null_count = titanic_survival.apply(null_count)
##例題10
- Create a function that returns the string
"minor"
if someone is under 18,"adult"
if they are equal to or over 18, and"unknown"
if their age is null. - Then, use the function along with
.apply()
to find the correct label for everyone in thetitanic_survival
dataframe. - Assign the result to
age_labels
. - You can use
pd.isnul
l to check if a value is null or not.
def labels_age(row):
age = row['age']
if pd.isnull(age):
return "unknown"
elif age < 18:
return "minor"
else:
return "adult"
age_labels = titanic_survival.apply(labels_age, axis=1)