LoginSignup
12
4

More than 5 years have passed since last update.

関数型でデータサイエンス#2:インプットしたデータを変換する

Last updated at Posted at 2018-06-03

(この記事は、「fukuoka.ex(その2) Elixir Advent Calendar 2017」の22日目、
  および「ディープラーニングのエンジニアリング Advent Calendar 2017」の6日目です)

昨日は、@takasehideki さんの「Erlang/OTPのソースビルドでHiPEが入らなかった時の対処法」でした


fukuoka.ex代表のpiacereです
今回もご覧いただいて、ありがとうございます:bow:

この連載の、前回までの記事は、以下になります
 |> 関数型でデータサイエンス#1:様々なデータをインプットする

今回は、インプットしたデータを、後続の分析処理で使いやすくするための「データ変換」をElixirを行うコードについて解説します

Elixirが、データサイエンスに向いた言語であることを実感する回となります


:shamrock::shamrock::shamrock::shamrock: お礼:各種ランキングに83回のランクインを達成しました :shamrock::shamrock::shamrock::shamrock:

4/27から、37日間に渡り、毎日お届けしている「季節外れのfukuoka.ex Elixir Advent Calendar」「季節外れのfukuoka.ex(その2) Elixir Advent Calender」ですが、Qiitaトップページトレンドランキングに12回入賞、Elixirウィークリーランキングでは6週連続で1/2/3フィニッシュを飾り、各種ランキング通算で、トータル83回ものランクインを達成しています

みなさまの暖かい応援に励まされ、合計552件ものQiita「いいね」もいただき、fukuoka.exアドバイザーズとfukuoka.exキャストの一同、ますます季節外れのfukuoka.ex Advent Calendar、頑張っていきます:rocket:

image.png

データ分析活動の要である「データ前処理」

前回もお伝えした通り、既にあるデータを、どのように搔き集め、データ分析側へと回すか、ということに、データ分析活動の80~90%が掛かっており、ここがうまくいかないと、機械学習やディープラーニングも、宝の持ち腐れとなります

こうした、「データ収集」と「データ前処理」は、需要がとても大きな領域で、中でも「データ前処理」は、エンジニアリングの比重がとても大きい、システム開発・運用・性能担保のノウハウを必要とする部分ですが、何故かクローズアップされる機会が、あまり多くありません

今回と次回で、この「データ前処理」を扱っていきますが、今回は、「インプットしたデータの変換」について取り上げます

なお、集約を伴う変換については、次回で扱いますので、今回は除外します

参考:「データ収集」と「データ前処理」以外の工程

データ分析/データサイエンスにおける工程として、「データ収集」の前に、「データ分析の目的設定」「分析計画」「分析設計とデータ設計」といった工程があります

また、「データ前処理」の後続には、「分析モデルの構築」「分析結果の評価」「未知データの分析」「未知データの分析結果の検証」といった工程があります

これら工程のうち、「分析モデルの構築」「分析結果の評価」については、本連載の範囲内として扱っていく予定です

その他の工程については、タイミングがあれば本連載中に、無ければ別の機会で扱いたいと思います

インプットデータの型の種類

インプットデータの各値には、以下のような、型の種類があります

  • 数値型
  • 文字列型
  • カテゴリ型
  • 日付・時刻型
  • 位置情報型
  • 画像・バイナリデータ型

「カテゴリ型」という名称に馴染みが無い方向けに解説すると、「red」「blue」「green」のような、バリエーションの個数が限られた文字列型を指します(enumのようなものをイメージしていただくと近いでしょう)

なお本連載では、CSVのような文字テキストデータを中心に扱うため、位置情報型と画像・バイナリデータ型は、対象外とし、本コラムでは、それ以外のデータ型についてのデータ変換を扱います

それでは、Phoenixサーバーを起動し、ブラウザで「http://localhost:4000」のWebページが見れる状態にしたら、以下を試していきましょう

サンプルデータ

前処理の処理サンプルに使うデータは、以下の少額ローン借入のサンプルデータが入ったCSVファイルとします

loan.csv
ID, Gender, Profession, ApplicantIncome, CoapplicantIncome, LoanAmount, LoanStartDatetime
1, Male, Unemployed, 307, 150, 126, 2001/05/01 13:24
2, Male, Employee, 500, 180, 208, 1998-12-02T17:46:21Z
3, , Management, 2340, 255, 100, 1989/02/03 15:54
4, Male, Employee, 328, 0, 78, 2000/06/01 12:23
5, Male,  SelfEmployed, 572, 0, 110, 2012/04/23 09:22:43
6, Male, Employee, 216, , 152, 2016/07/31 14:25:54
7, Female, Management, 2020, 222, 59, 2017-08-26 10:23:38Z
8, Male, Employee, 388, 0, 147, 2001/02/27 15:54:21
9, Male, SelfEmployed, 309, 0, 90, 2015/11/24 09:59:59
10, Female, SelfEmployed, 466, 0, 124, 2013-03-24 19:10:10Z
11, Male, Unemployed, 100, 566, 131, 2005/07/19 12:54:23
12, Male, SelfEmployed, 15, 291, 200, 2011/09/21 13:30
13, Male, Employee, 922, 791, 300, 2008-07-04 14:12:09Z
14, Male, Employee, 218, 151, 162, 2009/09/09 17:43:29
15, Male, Employee, 1217, 0, 166, 2012/11/09 11:43:15
16, Male, SelfEmployed, 130, 347, 100, 2014-10-27T14:54:22Z
17, Male, Unemployed, 188, 162, 48, 2000/03/23 15:41:54
18, , SelfEmployed, 390, 0, 101, 1999-12-24 13:34:43Z
19, Female, SelfEmployed, 376, 0, 125, 2012-10-08 13:11:12Z
20, Male, Employee, 540, 438, 290, 2014/01/23 14:33:45

各サンプルは、下記コードの「datas」作成部分を変更して、対応します

lib/sample_db_web/templates/page/index.html.eex
<%
result = "sample.csv"
  |> File.stream!
  |> CSV.decode( strip_fields: true, headers: true )
  |> Enum.map( &( elem( &1, 1 ) ) )
datas = result
columns = datas |> List.first |> Map.keys
%>
<table border="1">
<tr>
  <%= for column <- columns do %>
  <th><%= column %></td>
  <% end %>
</tr>
<%= for record <- datas do %>
<tr>
  <%= for column <- columns do %>
  <td><%= record[ column ] %></td>
  <% end %>
</tr>
<% end %>
</table>

「数値型」のデータ変換

数値型のデータ変換としては、以下がポピュラーです

  • 正規化
  • NULL値/外れ値の補完
  • NULL値/外れ値を含む行の削除

正規化

「正規化」とは、特定の値を基準とするための、各数値に対する加減乗除の実施を指します

以下は、該当列の最小値が1となるような除算を施す正規化を行う例です

lib/sample_db_web/templates/page/index.html.eex

min = result |> Enum.map( &( &1[ "ApplicantIncome" ] ) ) |> Enum.min |> String.to_integer
datas = result
  |> Enum.map( &( Map.put( &1, "ApplicantIncome", String.to_integer( &1[ "ApplicantIncome" ] ) / min ) ) )

NULL値/外れ値の補完

NULL値、もしくは外れ値と見なす数値を、適切な数値へと補完する場合は、以下のような操作を行います

なお、補完後の値には、その数値列全体の平均値/中央値、最大値/最小値、0などを用いることが多いですが、その選び方は、分析の業務内容によります

以下の例では、NULL値(空文字列)に対して、平均値を補完しました

lib/sample_db_web/templates/page/index.html.eex

sum = result |> Enum.filter( &( &1[ "CoapplicantIncome" ] != "" ) ) |> Enum.map( &( String.to_integer( &1[ "CoapplicantIncome" ] ) ) ) |> Enum.sum
average = sum / ( result |> Enum.map( &( &1[ "CoapplicantIncome" ] ) ) |> Enum.count )
datas = result
  |> Enum.map( &( Map.put( &1, "CoapplicantIncome", if &1[ "CoapplicantIncome" ] == "" do average else &1[ "CoapplicantIncome" ] end ) ) )

NULL値/外れ値を含む行の削除

NULL値、もしくは外れ値と見なす数値が含まれる行は除外したい、というケースもあり、以下のように行います

lib/sample_db_web/templates/page/index.html.eex

datas = result
  |> Enum.filter( &( &1[ "CoapplicantIncome" ] != "" ) )

「文字列型」のデータ変換

文字列型のデータ変換としては、以下がポピュラーです

  • 文字列両端のトリム
  • 特定文字列長でのカット
  • 文字列の(部分)置換
  • NULL値/外れ値の変換
  • NULL値/外れ値を含む行の削除

末尾2つは、数値型と同一の操作のため割愛します

文字列両端のトリム

以下のように、文字列両端のトリムを行います

lib/sample_db_web/templates/page/index.html.eex

datas = result
  |> Enum.map( &( Map.put( &1, "Profession", String.trim( &1[ "Profession" ] ) ) ) )

特定文字列長でのカット

以下のように、文字列のカットを行います

lib/sample_db_web/templates/page/index.html.eex

datas = result
  |> Enum.map( &( Map.put( &1, "Profession", String.slice( &1[ "Profession" ], 0..4 ) ) ) )

文字列の(部分)置換

以下のように、文字列の(部分)置換を行います

lib/sample_db_web/templates/page/index.html.eex

datas = result
  |> Enum.map( &( Map.put( &1, "Profession", String.replace( &1[ "Profession" ], "Un", "Not " ) ) ) )

「カテゴリ型」のデータ変換

カテゴリ型のデータ変換としては、以下がポピュラーです

  • カテゴリ値の数値化
  • NULL値/外れ値の補完
  • NULL値/外れ値を含む行の削除

末尾2つは、数値型と同一の操作のため割愛します

カテゴリ値の数値化

後続の処理として、機械学習/ディープラーニングに使用したい場合、文字列であるカテゴリ値を、数値に変換することで、特徴量として扱えるようにすることは、よくあるケースです

以下のように、まずカテゴリ値毎の通し番号のマップを作り、文字列を通し番号に変換します

lib/sample_db_web/templates/page/index.html.eex

categories = result
  |> Enum.map( &( &1[ "Profession" ] ) ) |> Enum.uniq |> Enum.with_index |> Enum.into( %{} )
datas = result
  |> Enum.map( &( Map.put( &1, "Profession", categories[ &1[ "Profession" ] ] ) ) )

「日付・時刻型」のデータ変換

日付・時刻型のデータ変換としては、以下がポピュラーです

  • デリミタの変換
  • 妥当で無い日付・時刻の補完
  • 妥当で無い日付・時刻を含む行の削除
  • NULL値/外れ値の変換
  • NULL値/外れ値を含む行の削除

2~3番目については、次回の集約と共に解説します

末尾2つは、数値型と同一の操作のため割愛します

デリミタの変換

日付や時刻を区切る文字(デリミタ)の変換は、文字列の(部分)置換と同等の変換を行います

lib/sample_db_web/templates/page/index.html.eex

datas = result
  |> Enum.map( &( Map.put( &1, "LoanStartDatetime", String.replace( &1[ "LoanStartDatetime" ], "-", "/" ) ) ) )

終わり

今回は、「データ前処理」のうち、「インプットしたデータの変換」について取り上げました

次回は、「データ前処理」の続きとして、「インプットしたデータの集約」について解説します

明日は @tuchiro さんの「ElixirでSI開発入門 #9 Railsからのモデルの移行2(DDLをパースする)」です

:stars::stars::stars::stars::stars: お知らせ:Elixir MeetUpを6月末に開催します :stars::stars::stars::stars::stars:

「fukuoka.ex#11:DB/データサイエンスにコネクトするElixir」を6/22(金)19時に開催します

fukuoka.exの発足から、ちょうど1周年となる、記念的なイベントでもあるため、このコラムを気に入っていただいた方と、ぜひ一緒に盛り上がりたいです

福岡近辺にお住まいの方であれば、遊びに来てください

大人気により、一度は満席となりましたが、増枠しましたので、下記URLよりご参加ください
https://fukuokaex.connpass.com/event/87241

特別ゲストは、Erlang/Elixirの両面で世界的に有名な「力武 健次さん」と、北九州の飯塚市で「e-ZUKA Tech Night」を6年間主催し続けるハウインターナショナルの「谷口 耕平さん」のお二人と、実に豪華なイベントです

私は、今回のシリーズを踏まえた、「1家に1台、パーソナルなデータ分析AIを全員が持つ2020年を作る」というタイトルで、Elixirによる、ブラウザUI上からサクっとデータ分析プラットフォームを披露するLTをお届けします

image.png

p.s.「いいね」よろしくお願いします

よろしければ、ページ左上の image.pngimage.png のクリックをお願いしますー:bow:
ここの数字が増えると、書き手としては「ウケている」という感覚が得られ、連載を更に進化させていくモチベーションになりますので、もっとElixirネタを見たいというあなた、私たちと一緒に盛り上げてください!:tada:

12
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
4