In [ ]:
import io

import numpy as np
import pandas as pd
import plotly_express as px

In [ ]:
# MASTER ONLY
import ast
# imports %%solution, %%submission, %%template etc.
%load_ext prog_edu_assistant_tools.magics
from prog_edu_assistant_tools.magics import report, autotest

Data frames 3: 簡単なデータの変換 (Simple data manipulation)

# ASSIGNMENT METADATA
assignment_id: "DataFrame3"

lang:en In this unit, we will get acquainted with a couple of simple techniques to change the data:

  • Filter rows based on a condition
  • Create new columns as a transformation of other columns
  • Drop columns that are no longer needed

Let's start with reading the data.

lang:ja この講義では、簡単なデータの変換を紹介します。

  • 行を条件によりフィルター(抽出)します
  • データ変換によって新しい列を作ります
  • 必要だけの列を抽出します

まずはデータを読み込みましょう。


In [ ]:
# データをCVSファイルから読み込みます。 Read the data from CSV file.
df = pd.read_csv('data/15-July-2019-Tokyo-hourly.csv')

In [ ]:
print("データフレームの行数は %d" % len(df))
print(df.dtypes)
df.head()

lang:en Let's consider the question of how one should hold an umbrella when it rains. Depending on the wind direction, it's better to slant the umbrella towards the direction the rain is coming from. Therefore, one needs to know the wind direction when it rains. First step is to limit the data to the hours when there was rain. To accomplish that, we filter the data set by using a condition. The condition is placed in square brackets after the dataframe.

Technical details:

  • The inner df['Precipitation_mm'] extracts a single column as a pandas Series object.
  • The comparison df['Precipitation_mm'] > 0' is evaluated as a vector expression, that computes the condition element-wise, resulting in a Series object of the same length with boolean elements (true or false).
  • Finally, the indexing of a data frame by the boolean series performs the filtering of the rows in the dataframe only to rows which had the corresponding element as True. Note that the original data frame is left unmodified. Instead, a new copy of a data

lang:ja雨の中の傘の持ち方について考えましょう。風の向きによって、適切な持ち方が変わります。風が来ている方向に傾けると傘の効率がよくなります。 したがって、雨のときの風の向きを調べなければいけません。 まずは雨のなかったデータを除きましょう。そのために条件をつけてデータをフィルターします。 条件はデータフレームの参照の後に角括弧に入ります。

詳しく述べると:

  • 角括弧に入っているdf['Precipitation_mm']は一つの列を抽出します。それはpandasのSeriesオブジェクトになります。
  • 比較表現 df['Precipitation_mm'] > 0' は各行ごとに評価されます、真理値のベクターになります。それもSeriesです。長さはデータフレームの行数です。
  • データフレームの後に角括弧に真理値ベクターを入れるとFalseの行が除かれます。

結果のデータフレームは新しいデータフレームです。既存のデータフレームは変わらないままで、フィルターされたデータフレームを新しい変数に保存します。


In [ ]:
# This is an example of filtering rows by a condition
# that is computed over variables in the dataframe.
# 条件によってデータフレームをフィルターします。
df2 = df[df['Precipitation_mm'] > 0]
len(df2)

lang:en So it was 11 hours out of 24 in a day that the rain was falling. Let's see what the distribution of wind directions was.

lang:ja 一日の24時間の中に雨が降っていたは11時間がありました。 風の向きを可視化しましょう。 px.histogramxの値を数えて、個数を棒グラフとして可視化します。


In [ ]:
px.histogram(df2, x='WindDirection_16compasspoints')

lang:en Now we can clearly see that NE was the prevailing wind direction while it rained.

Note that the result may have been different if we did not filter for the hours with rain:

lang:ja雨が降ったときに風はNEの方向に吹いたことがわかります。雨だけのデータにフィルターしなければ、グラフは異なる結果がえられます。 以下はdfは元のデータフレームで、フィルターされたデータフレームはdf2です。


In [ ]:
px.histogram(df, x='WindDirection_16compasspoints')

lang:en We can plot the whole data and use the color dimension to distinguish between hours when it rained or not by using a different technique: instead of filtering rows by some condition, we can introduce the condition as a new boolean variable. This is done by assigning to a new column in the data frame:

lang:jaフィルターに変わりに、可視化によって同じデータを確認ができます。たとえば、雨が降ったかどうかを色で表現します。 そのために新しい真理値の列を作らなければなりません。以下の例はdfのデータフレームに新しい列を追加します。


In [ ]:
# This creates a new column named "rained" that is a boolean variable 
# indicating whether it was raining in that hour.
# 新しい真理値の列'rained'を追加します。
df['rained'] = df['Precipitation_mm'] > 0
px.histogram(df, x='WindDirection_16compasspoints', color='rained')

lang:en Now let's consider how could we present the same data in a tabular form. If we do not do anything, all existing columns in the data frame would be shown, which may make it hard for the reader to see the point of the author. To make reading the data easier, we can limit the data output just to columns we are interested in.

lang:ja 今まで解析してきたデータを表の形に表示について考えましょう。 dfのデータフレームをそのまま表示するとたくさんの列が出て、 どのデータを見せたかったのはとてもわかりにくくなります。 それを解決するために、見せたい列だけを抽出しましょう。


In [ ]:
# そのままだとデータが多すぎて混乱しやすい。
# その表を見せてなにがいいたいのか分かりづらい。
df

In [ ]:
# 列の名前の一覧を見ましょう。
df.dtypes

In [ ]:
# Indexing by list of column names returns a copy of the data frame just with the named
# columns.
# 列の名前を二重角括弧に入れると、列の抽出ができます。 列の名前は以上の`dtypes`の一覧によって確認できます。
df[['Time_Hour', 'WindDirection_16compasspoints', 'rained']]

予習課題: データの変換 (Data manipulation)

# EXERCISE METADATA
exercise_id: "DataManipulation"

lang:en Starting with the weather data frame df defined above, filter out the data set consisting only of the day hours when sun was shining (i.e. variable SunshineDuration_h > 0), and containing only the following columns:

  • Time_Hour -- extracted from the original data frame.
  • WindDirection_16compasspoints -- extracted from the original data frame.
  • rained -- the boolean indicator of whether it was raining or not (Precipitation_mm > 0). This is a new column that is not present in the original data, so it should be added.

lang:ja 以上に定義したdfのデータフレームを使って、以下のデータの表を抽出しましょう。

  • 日が出ていた時間帯のみ (すなわち、SunshineDuration_h > 0)

以下の列だけを抽出しましょう。

  • Time_Hour -- 元のデータフレームから抽出しましょう。
  • WindDirection_16compasspoints -- 元のデータフレームから抽出しましょう。
  • rained -- 雨があったかどうかの真理値列 (すなわち、Precipitation_mm > 0)。こちらの列は元のデータに入ってないため、追加しなければなりません。

In [ ]:
%%solution
""" # BEGIN PROMPT
# Note: you can do multiple steps to get the data frame you need.
# 複数の段階に分けてデータ処理してもよい。
df['rained'] = df[...]
sunny_df = df[...]
sunny_df = sunny_df[...]
""" # END PROMPT
# BEGIN SOLUTION
df['rained'] = df['Precipitation_mm'] > 0
sunny_df = df[df['SunshineDuration_h'] > 0]
sunny_df = sunny_df[['Time_Hour', 'WindDirection_16compasspoints', 'rained']]
# END SOLUTION

lang:enNote: if you see a warning SettingWithCopyWarning, it means that you are trying to apply transformation to a data frame that is a copy or a slice of a different data frame. This is an optimization that Pandas library may do on filtering steps to reduce memory use. To avoid this warning, you can either move the new column computation before the filtering step, or add a .copy() call to the filtered data frame to force creating of a full data frame object.

lang:jaもしSettingWithCopyWarningのエラーが出たら、データフレームのコピーに変更を行うという意味なのです。pandasは、データ抽出のときに 自動的にコピーしないような最適化の副作用です。解決のために、データ変更は先にするか、抽出の後に.copy()を呼び出すことができます。


In [ ]:
# Inspect the data frame
sunny_df

In [ ]:
%%studenttest StudentTest
# Test your solution
assert len(sunny_df) == 2, "The result data frame should only have 2 rows, yours has %d" % len(sunny_df)
assert np.sort(np.unique(sunny_df['Time_Hour'])).tolist() == [13, 14], "Sunshine was during 13h,14h, but you got %s" % sunny_df['Time_Hour']
assert np.all(sunny_df['rained'] == False), "It was not raining during sunshine hours!"

In [ ]:
%%inlinetest AutograderTest

# This cell will not be present in the students notebook.

assert 'sunny_df' in globals(), "Did you define the data frame named 'sunny_df' in the solution cell?"
assert sunny_df.__class__ == pd.core.frame.DataFrame, "Did you define a data frame named 'sunny_df'? 'sunny_df' was a %s instead" % sunny_df.__class__
assert len(sunny_df) == 2, "The data frame should have 2 rows, but you have %d" % len(sunny_df)
assert np.sort(np.unique(sunny_df['Time_Hour'])).tolist() == [13, 14], "Sunshine was during 13h,14h, but you got %s" % sunny_df['Time_Hour']
assert np.all(sunny_df['rained'] == False), "It was not raining during sunshine hours!"
assert np.all(np.sort(np.unique(sunny_df.columns)) == ['Time_Hour', 'WindDirection_16compasspoints', 'rained']), ("Expected to see 3 columns: rained, Time_Hour, WindDirection_16compasspoints, but got %d: %s" % (len(np.unique(sunny_df.columns)), np.sort(np.unique(sunny_df.columns))) )

In [ ]:
%%submission
df['rained'] = df['Precipitation_mm'] > 0
sunny_df = df[df['SunshineDuration_h'] > 0]
#sunny_df = sunny_df[['Time_Hour', 'WindDirection_16compasspoints', 'rained']]

In [ ]:
import re
result, logs = %autotest AutograderTest
assert re.match(r'Expected to see 3 columns.*', str(result.results['error']))
report(AutograderTest, results=result.results, source=submission_source.source)