Rで学ぶ計量経済学と機械学習 2

データ整理

This version: 2026-10-08

Author

安藤道人(立教大学) 三田匡能 (株式会社 GA technologies)

1 本講義の目的

  • パッケージについて学ぶ
  • パイプ演算子について学ぶ
  • dplyrやtidyverseを使ったデータの前処理について学ぶ

2 パッケージ

パッケージ(package)とは、Rの機能を拡張するプログラムである。 (他のプログラミング言語ではライブラリと呼ぶものにあたる)

2.1 パッケージのセットアップ

公開されているパッケージをダウンロードしてインストールするにはinstall.packages("パッケージ名")と書き、インストールしたパッケージを読み込むにはlibrary(パッケージ名)と書く。

なお、インストールは一度すれば次回からはinstall.packages()を実行する必要はないが、library()はRを起動するたびに必要になる。なお、install.packages()やlibrary()を使わないでパッケージを読み込む方法も、後日学びます。

さて、以下ではExcelファイルを読み込むためのパッケージ{readxl}を使用していく。

# パッケージ読み込み
install.packages("readxl") #インストール
library("readxl") # 読み込み

2.1.1 ファイルのアップロード

まず、分析したいデータファイルを作業用フォルダに入れる。今回は、講義ページにある{test_scores.xlsx}のファイルを使う。

なお、Posit Cloudのワークスペースにアップロードする場合は、右下の[Files]タブの[Upload]をクリックしてファイルをアップロードする。

test_scores.xlsxをアップロードすれば、[Files]タブにも反映される。

2.2 エクセル(.xlsx)ファイルの読み込み

{readxl}パッケージのread_excel()関数で読み込みを行う。

# エクセル読み込み
df <- read_excel("test_scores.xlsx")

3 データフレームを概観する

3.1 データフレームオブジェクトの確認

オブジェクトdfに代入されたデータフレームは[Environment]タブにも表示される。

「40 obs. of 5 variables」とは、「観測値(行)が40、変数(列)が5」のデータフレームであることを示している。

3.2 行数・列数を確認

データフレームオブジェクトの行数と列数を関数で確認したいときはdim()を使う

dim(df)
[1] 40  5

(行数だけを取得するnrow()や列数だけを取得するncol()といった関数もある)

3.3 View()

「40 obs. of 5 variables」の右にある表のようなアイコンをクリックする(あるいはView(df)を実行する)と、エクセルのようなセルにデータが入った形式でデータフレームを表示することができる。

4 パイプ演算子

Rは基本的に関数を使ってコードを書くため、複数の処理をまとめて書くときは関数の中に関数を内包する形になる。

複雑な処理を行うときは、関数を内包するたびにコードがどんどん読みづらくなっていく。

head(read_excel("test_scores.xlsx"))
# A tibble: 6 × 5
  class name   math english japanese
  <chr> <chr> <dbl>   <dbl>    <dbl>
1 C     安藤     69      78       73
2 A     藤村     61      60       60
3 B     大泉     77      83       61
4 B     木村     45      49       68
5 A     鈴井     52      25       52
6 C     望月     79      65       74

そこでパイプ演算子(|>)を利用する。 |>はR 4.1以降であれば追加のパッケージなしで使える、base R(Rの標準機能)の演算子である。

以降の章では{dplyr}パッケージのfilter()やselect()といった便利な関数もあわせて使っていくので、ここでインストール・読み込みをしておく。

# パッケージのインストール
install.packages("dplyr")
# パッケージの読み込み
library(dplyr)

パイプ演算子は左にあるオブジェクトを右の関数に受け渡して処理をさせることができる。

read_excel("test_scores.xlsx") |> head()
# A tibble: 6 × 5
  class name   math english japanese
  <chr> <chr> <dbl>   <dbl>    <dbl>
1 C     安藤     69      78       73
2 A     藤村     61      60       60
3 B     大泉     77      83       61
4 B     木村     45      49       68
5 A     鈴井     52      25       52
6 C     望月     79      65       74

なおRStudioではShift + Ctrl + M(MacはShift + Command + M)でパイプ演算子を挿入することができる(最近のRStudioではデフォルトで|>が挿入されるが、[Tools] → [Global Options] → [Code]の設定で%>%に変更することもできる)。

パイプの始点は関数でなくオブジェクトでもよい。

# subset()は条件(classがCである)に合う行を取り出す関数
df |> subset(class == "C") |> head()
# A tibble: 6 × 5
  class name    math english japanese
  <chr> <chr>  <dbl>   <dbl>    <dbl>
1 C     安藤      69      78       73
2 C     望月      79      65       74
3 C     嬉野      61      61       59
4 C     安田      71      73       65
5 C     伊藤      79      52       55
6 C     津久井    62      65       52

なお、パイプ処理で関数に渡されるオブジェクトは関数の第一引数に入れられる。 (本来subset(データフレーム, 条件)となっていて、この「データフレーム」が第一引数)

ただし、subset(, 条件)のようにカンマを残す必要はなく、subset(条件)のように書けばよい。

4.1 (参考){magrittr}のパイプ演算子%>%

{dplyr}などのtidyverse系パッケージでは、もともと{magrittr}パッケージ由来の%>%というパイプ演算子が使われてきた。|>が導入される前の解説記事やパッケージの公式ドキュメントには今も%>%が数多く使われているため、読めるようにしておくとよい。

# %>% も |> とほぼ同じ働きをする
read_excel("test_scores.xlsx") %>% head()
# A tibble: 6 × 5
  class name   math english japanese
  <chr> <chr> <dbl>   <dbl>    <dbl>
1 C     安藤     69      78       73
2 A     藤村     61      60       60
3 B     大泉     77      83       61
4 B     木村     45      49       68
5 A     鈴井     52      25       52
6 C     望月     79      65       74

基本的な使い方であれば|>と%>%はほぼ同じ結果になるので、本講義では以降|>に統一して表記する。

5 データフレームの操作

5.1 {tidyverse}パッケージ

データの本格的な分析に入る前にデータの整理や加工を行うことを前処理(preprocess)という。

Rでは前処理を効率よく行うために{dplyr}や{tidyr}や{stringr}などのパッケージが整備されている。

いくつものパッケージを個別にインストールと読み込みを行うのは面倒なので、{readxl}や{dplyr}や{stringr}などの便利なパッケージをまとめた{tidyverse}というパッケージをインストールする。

install.packages("tidyverse") #インストール
library("tidyverse") # 読み込み

{tidyverse}パッケージで読み込まれるパッケージは、例えば次のようなものである

  • 前処理を便利にするもの
    • dplyrやtidyr:データフレームの操作のためのパッケージ
    • stringr:文字列の操作のためのパッケージ
  • 既存の関数の機能拡張
    • readr:read.csv()関数をもっと便利なものに置き換える
    • readxl:Excelのファイル(.xlsx)を読み込む関数を提供する
    • tibble:data.frame()関数をもっと便利なものに置き換える
    • ggplot2 :綺麗なグラフやGIS(統計地図)などを描くことができる

5.2 条件指定による行の取り出し

5.2.1 条件と論理演算

ここでいう条件とは、「変数Xが2である(\(X = 2\))」「変数Zが0以上である(\(Z \geq 0\))」などといったもののこと。

Rでは等号や不等号は次のように表す。

数学上の記号 Rでの記法 意味
=、≠ ==、!= である、でない
>、< >、< より大きい、より小さい
≧、≦ >=、<= 以上、以下

これらの条件を複数つなげるとき(例えば「\(X=2\)かつ\(Z=10\)」のようにするとき)の論理演算子は次のように書く。

数学上の記号 Rでの記法 意味
∧、∩ & かつ(and)
∨、∪ | または(or)
# 条件式の計算の例
x <- 7            # x = 7と定義
x != 2            # 「xは2ではない」(か否か)
[1] TRUE
(x > 2) & (x < 4) # 「2より大きい」かつ「4より小さい」(であるか否か)
[1] FALSE

条件式の計算結果はTRUE(真)とFALSE(偽)の値(論理値)で返される。

5.2.2 関数を使わないで行指定

条件を満たす行を抜き出したいとき、関数を使わずにシンプルに記述する場合は次の方法で行う。

df[df["class"] == "C",] # classがCの行を取り出す
# A tibble: 15 × 5
   class name    math english japanese
   <chr> <chr>  <dbl>   <dbl>    <dbl>
 1 C     安藤      69      78       73
 2 C     望月      79      65       74
 3 C     嬉野      61      61       59
 4 C     安田      71      73       65
 5 C     伊藤      79      52       55
 6 C     津久井    62      65       52
 7 C     松沢      83      91       68
 8 C     平藤      49      71       59
 9 C     荻下      74      50       48
10 C     岸        90      65       70
11 C     鈴木      72      50       55
12 C     山津      57      65       34
13 C     木村      71      80       72
14 C     望月      58      55       62
15 C     増田      72      56       64

これは、dfというデータフレームの"class"という列(df["class"])が"C"であるか否かをdf["class"] == "C"として計算させて論理値(TRUEやFALSE)のベクトルを得て、それをdf[行番号,列番号]の行番号の部分に入れることで条件に合致する行を取り出す方法。

5.2.3 関数を使って行指定

{dplyr}パッケージのfilter()関数を使っても同様に条件指定して行を取り出すことができる。

filter(データフレーム, 条件式)の形で書く。

dplyr::filter(df, 50 < english & english < 60) # englishが50~60点の人を抽出
# A tibble: 7 × 5
  class name   math english japanese
  <chr> <chr> <dbl>   <dbl>    <dbl>
1 C     伊藤     79      52       55
2 A     後藤     79      51       59
3 A     阿部     62      58       44
4 A     豊田     60      56       78
5 C     望月     58      55       62
6 B     平井     56      56       73
7 C     増田     72      56       64

(filter()はパイプ処理の節で登場したsubset()と機能も文法もほぼ一緒)

なお、dplyr::は省略することもできる。ただし、読み込んでいる他のパッケージに同じ名前の関数があった場合、エラーが生じるので、できるだけ省略しないことをすすめる。ただ、本講義では簡便化のためにわりと省略している。

filter(df, 50 < english & english < 60) # englishが50~60点の人を抽出
# A tibble: 7 × 5
  class name   math english japanese
  <chr> <chr> <dbl>   <dbl>    <dbl>
1 C     伊藤     79      52       55
2 A     後藤     79      51       59
3 A     阿部     62      58       44
4 A     豊田     60      56       78
5 C     望月     58      55       62
6 B     平井     56      56       73
7 C     増田     72      56       64

5.2.3.1 (参考)filterの派生関数群

if_any()、if_all()といった関数を組み合わせることでより複雑なフィルタリング条件を簡潔に指定することもできる。

# データフレームのいずれかの変数で100という値をとる行
# (参考)やや古い書き方になるが filter_all(df, any_vars(. == 100)) と書くこともできる
filter(df, if_any(everything(), ~ . == 100))
# A tibble: 1 × 5
  class name   math english japanese
  <chr> <chr> <dbl>   <dbl>    <dbl>
1 B     橋本     73      95      100

if_any(対象の列を指定する関数, 条件や処理を指定する関数) というやや難しい記法になる。

(~ . == 100は{purrr}パッケージで導入される無名関数の記法で、.を引数に取る関数function(.){. == 100}を定義していることと等しい)

対象の列は、ends_with()やcontains()のような部分一致だけでなく、all_of()で列名を直接指定することもできる。

# englishとjapaneseの2列がどちらも90以上の値をとる行を取り出す
# if_all()をif_any()に変えると「いずれかで90以上の値」という条件になる
df |> filter(if_all(all_of(c("english", "japanese")), ~ . > 90))
# A tibble: 1 × 5
  class name   math english japanese
  <chr> <chr> <dbl>   <dbl>    <dbl>
1 B     橋本     73      95      100

5.3 列の取り出し

select()関数はデータフレームから条件に合う列を取り出す関数である

基本的にはselect(データフレーム, 列名1, 列名2, ...)のように書く。

dplyr::select(df, class, name)
# A tibble: 40 × 2
   class name 
   <chr> <chr>
 1 C     安藤 
 2 A     藤村 
 3 B     大泉 
 4 B     木村 
 5 A     鈴井 
 6 C     望月 
 7 C     嬉野 
 8 B     山口 
 9 C     安田 
10 A     新城 
# ℹ 30 more rows

パイプ演算子を使用してもよい。

df |> select(class, name)
# A tibble: 40 × 2
   class name 
   <chr> <chr>
 1 C     安藤 
 2 A     藤村 
 3 B     大泉 
 4 B     木村 
 5 A     鈴井 
 6 C     望月 
 7 C     嬉野 
 8 B     山口 
 9 C     安田 
10 A     新城 
# ℹ 30 more rows

5.3.1 除外条件の指定

列名を指定するときに、マイナス(-)をつけると、「その列以外を取り出す」という指定になる

df |> dplyr::select(-name)
# A tibble: 40 × 4
   class  math english japanese
   <chr> <dbl>   <dbl>    <dbl>
 1 C        69      78       73
 2 A        61      60       60
 3 B        77      83       61
 4 B        45      49       68
 5 A        52      25       52
 6 C        79      65       74
 7 C        61      61       59
 8 B        85      85       84
 9 C        71      73       65
10 A        79      79       78
# ℹ 30 more rows

5.3.2 列名を検索して指定

select()の中でcontains()という関数を使うと、「列名に〇〇という言葉を含むものを取り出す」という指定を行うことができる。

df |> select(contains("an")) # "an"を含む列名(japanese)を取り出す
# A tibble: 40 × 1
   japanese
      <dbl>
 1       73
 2       60
 3       61
 4       68
 5       52
 6       74
 7       59
 8       84
 9       65
10       78
# ℹ 30 more rows

5.4 列名の変更

データフレームの列名を変更したいときは、主に次の2つの方法がある。

Excelファイルなどの元データでは、列名が日本語になっていることもよくある。変数名に日本語(特に全角記号や漢字)を使うと、異なる環境で文字化けやエラーの原因になったり、日本語が読めない人にとって読みにくくなったりするため、読み込んだ後になるべく早い段階で英語名に変換しておくことをすすめる。ここでは、そうした日本語の列名を英語名に変換する場面を例に、列名の変更方法を紹介する。

5.4.1 colnames()を使う

colnames()関数を使うとデータフレームの列名を取得できる(なお、行名はrownames()で取得できる)

# 列名が日本語になっている例(説明のために作成したデータ)
df_ja <- data.frame(クラス = c("B","A","A"),
                    英語 = c(79, 91, 89),
                    数学 = c(75, 81, 92))
colnames(df_ja) # 列名の表示
[1] "クラス" "英語"   "数学"  

逆に、colnames()に(列数と同じ長さの)文字列ベクトルを指定することでデータフレームの列名を変更できる。

colnames(df_ja) <- c("class", "english", "math") # 列名を英語名に変更
df_ja
  class english math
1     B      79   75
2     A      91   81
3     A      89   92

5.4.2 rename()を使う

rename()関数を使い、rename(df, 新しい列名 = 古い列名)のように指定すると任意の列の列名のみを変えることができる。

df2 <- df |> select(class, name) # 説明の簡略のために2列を取り出す
df2 |> dplyr::rename(NAME = name)
# A tibble: 40 × 2
   class NAME 
   <chr> <chr>
 1 C     安藤 
 2 A     藤村 
 3 B     大泉 
 4 B     木村 
 5 A     鈴井 
 6 C     望月 
 7 C     嬉野 
 8 B     山口 
 9 C     安田 
10 A     新城 
# ℹ 30 more rows

5.5 行を並べ替える

「点数が低い順に並べ替える」といった操作を行いたい場合、arrange()を使う

df |> dplyr::arrange(math) # mathの点が低い順(昇順)に並べ替える
# A tibble: 40 × 5
   class name   math english japanese
   <chr> <chr> <dbl>   <dbl>    <dbl>
 1 B     前田     35      36       48
 2 B     木村     45      49       68
 3 B     岩田     46      32       57
 4 C     平藤     49      71       59
 5 B     福井     50      65       52
 6 A     鈴井     52      25       52
 7 B     野沢     52      71       99
 8 A     赤石     53      64       56
 9 B     平井     56      56       73
10 C     山津     57      65       34
# ℹ 30 more rows

「高い順に並べ替える」という操作をしたい場合はarrange()の中でdesc()を使う

df |> arrange(desc(english)) # englishの点が高い順(降順)に並べ替える
# A tibble: 40 × 5
   class name   math english japanese
   <chr> <chr> <dbl>   <dbl>    <dbl>
 1 B     橋本     73      95      100
 2 A     桜井     83      92       72
 3 C     松沢     83      91       68
 4 B     吉川     83      88       80
 5 B     磯部     62      87       75
 6 B     金田     81      87       89
 7 B     岩村     69      86       82
 8 B     山口     85      85       84
 9 B     大泉     77      83       61
10 B     砂原     74      80       75
# ℹ 30 more rows

5.6 新たな列を追加する

前処理においては、「変数を加工し、新たな列に追加する」という処理を行う機会が多い。

変数の加工の仕方は目的に応じて様々だが、ここでは例として学生ごとの3科目の合計得点を算出する、

5.6.1 1列ずつ追加する

各科目の列を取り出して足し合わせれば合計得点の算出はできるので、それをdf["新たな列の名前"]のように記述したものに代入すれば列の追加は簡単に終わる

# 総得点
df["total_score"] <- df$math + df$english + df$japanese

# 列が追加された
head(df)
# A tibble: 6 × 6
  class name   math english japanese total_score
  <chr> <chr> <dbl>   <dbl>    <dbl>       <dbl>
1 C     安藤     69      78       73         220
2 A     藤村     61      60       60         181
3 B     大泉     77      83       61         221
4 B     木村     45      49       68         162
5 A     鈴井     52      25       52         129
6 C     望月     79      65       74         218

ただし、この方法では追加したい列の数だけこの記述を繰り返す必要がある。

5.6.2 複数列まとめて追加する

mutate()関数を使うと列の追加をもう少しスマートに記述できて、mutate(df, 新しい列の名前 = 代入したい内容)のように書く。

例えばmath+japanese、math+englishの2つの変数を作り、それぞれmath_japanese、math_englishと名付けることにする。

その場合、次のようにカンマで区切りながら変数を追加していく。

df |> dplyr::mutate(math_japanese = math + japanese,
                     math_english = math + english)
# A tibble: 40 × 8
   class name   math english japanese total_score math_japanese math_english
   <chr> <chr> <dbl>   <dbl>    <dbl>       <dbl>         <dbl>        <dbl>
 1 C     安藤     69      78       73         220           142          147
 2 A     藤村     61      60       60         181           121          121
 3 B     大泉     77      83       61         221           138          160
 4 B     木村     45      49       68         162           113           94
 5 A     鈴井     52      25       52         129           104           77
 6 C     望月     79      65       74         218           153          144
 7 C     嬉野     61      61       59         181           120          122
 8 B     山口     85      85       84         254           169          170
 9 C     安田     71      73       65         209           136          144
10 A     新城     79      79       78         236           157          158
# ℹ 30 more rows

5.7 データフレームの連結

5.7.1 横方向の連結

同じ行数の2つのデータフレームを連結したいときはbind_cols()を使う

# データフレームを分割
A <- df |> select(class, name)
B <- df |> select(math, english, japanese)

# データフレームの連結
bind_cols(A,B)
# A tibble: 40 × 5
   class name   math english japanese
   <chr> <chr> <dbl>   <dbl>    <dbl>
 1 C     安藤     69      78       73
 2 A     藤村     61      60       60
 3 B     大泉     77      83       61
 4 B     木村     45      49       68
 5 A     鈴井     52      25       52
 6 C     望月     79      65       74
 7 C     嬉野     61      61       59
 8 B     山口     85      85       84
 9 C     安田     71      73       65
10 A     新城     79      79       78
# ℹ 30 more rows

(ちなみに、同様の機能をもつ関数でcbind()というものもある。)

5.7.2 縦方向の連結

同じ列数の2つのデータフレームを連結したいときはbind_rows()を使う

# データフレームを分割
A <- df[1:20, ]   # 第1~20行
B <- df[21:40, ]  # 第21~40行

# データフレームの連結
bind_rows(A, B)
# A tibble: 40 × 6
   class name   math english japanese total_score
   <chr> <chr> <dbl>   <dbl>    <dbl>       <dbl>
 1 C     安藤     69      78       73         220
 2 A     藤村     61      60       60         181
 3 B     大泉     77      83       61         221
 4 B     木村     45      49       68         162
 5 A     鈴井     52      25       52         129
 6 C     望月     79      65       74         218
 7 C     嬉野     61      61       59         181
 8 B     山口     85      85       84         254
 9 C     安田     71      73       65         209
10 A     新城     79      79       78         236
# ℹ 30 more rows

(同様の機能をもつ関数でrbind()というものもある。)

5.8 (参考)データフレームの結合

「異なる行数・列数のデータフレームが2つあり、“id”列だけが対応関係にある」というような状況は公的統計でも民間企業のデータでも非常に多くある。 (例えば公的統計なら「“地方自治体コード”列が対応関係にあるデータ群」であったり、民間企業なら「“顧客id”列が対応関係にあるデータ群」であったりする。)

そうした場合は”id”列を基準にデータフレームを結合するjoin関数を使う。

5.8.1 dplyr::full_join()

full_join()関数を例に実行してみる。

まず、例示用に簡単なデータフレームを作る

# 顧客データ
customers <- data.frame(顧客id = 1:3,
                        性別 = c("男性", "女性", "女性"),
                        年齢 = c(20, 24, 35))
customers
  顧客id 性別 年齢
1      1 男性   20
2      2 女性   24
3      3 女性   35
# 取引データ
sales <- data.frame(取引id = 1:3,
                    顧客id = c(1,2,2),
                    商品 = c("みかん", "みかん", "りんご"),
                    金額 = c(200, 200, 300))
sales
  取引id 顧客id   商品 金額
1      1      1 みかん  200
2      2      2 みかん  200
3      3      2 りんご  300

これをfull_join()で結合する。full_join(df1, df2, by = "id")のように書く。

# "顧客id"をキーにcustomersとsalesを結合
dplyr::full_join(customers, sales, by = "顧客id")
  顧客id 性別 年齢 取引id   商品 金額
1      1 男性   20      1 みかん  200
2      2 女性   24      2 みかん  200
3      2 女性   24      3 りんご  300
4      3 女性   35     NA   <NA>   NA

このように顧客idで紐づけしてひとつのデータフレームへと結合する。

5.8.2 結合の種類

ところで、結合する2つのデータフレーム間でidのデータが等しいとは限らない。 一方では他方に比べて収録されているidのデータが少ないかもしれない (先の例で顧客id == 3の取引履歴が存在しなかったように)。

結合の方法は一般的に4種類存在する。 Rでは結合方法に応じて別々の関数になっており、

  1. inner_join(): 両方のデータフレームに存在するidを使う
  2. full_join(): いずれかのデータフレームに存在するidを使う
  3. left_join(): 左側に指定されたデータフレームに存在するidを使う
  4. right_join(): 右側に指定されたデータフレームに存在するidを使う

となっている。

5.8.3 inner_join()

inner_join()を使えば、先程の例での顧客id == 3は一方のデータフレームにしか存在しないidであるため、結合後は消える

dplyr::inner_join(customers, sales, by = "顧客id")
  顧客id 性別 年齢 取引id   商品 金額
1      1 男性   20      1 みかん  200
2      2 女性   24      2 みかん  200
3      2 女性   24      3 りんご  300

逆にfull_join()では、先に示したように、顧客id == 3の行が残り、紐づくデータが無い列はNA(欠損値)になる

5.9 (参考)データフレームの展開

データの形式には「ロング形式(縦持ち)」と呼ばれるものと、「ワイド形式(横持ち)」と言われるものがある。

ロング形式は、例えば上記のsalesデータのように”商品”列には商品の種類を書き、“金額”列にはその商品の売上金額を書くものである。

一般的なデータが基本的に採用している形式で、これまで例に使用してきたデータフレームもロング形式である。

# ロング形式
sales
  取引id 顧客id   商品 金額
1      1      1 みかん  200
2      2      2 みかん  200
3      3      2 りんご  300

ワイド形式は、例えばsalesデータで”商品”列の商品の種類ごとに列を分け、“顧客id”に対応する金額をセルに入れたもの(クロス集計表のような形式になっているもの)である。

{tidyr}パッケージのpivot_wider()でワイド形式に変換することができる

# ワイド形式
sales_wide <- sales |> tidyr::pivot_wider(names_from = "商品", values_from = "金額")
sales_wide
# A tibble: 3 × 4
  取引id 顧客id みかん りんご
   <int>  <dbl>  <dbl>  <dbl>
1      1      1    200     NA
2      2      2    200     NA
3      3      2     NA    300

{tidyr}パッケージのpivot_longer()でロング形式に戻すことができる

# ロング形式に戻す
sales_long <- sales_wide |>
  tidyr::pivot_longer(cols = c(-顧客id, -取引id), names_to = "商品", values_to = "金額")

sales_long |> dplyr::filter(!is.na(金額))
# A tibble: 3 × 4
  取引id 顧客id 商品    金額
   <int>  <dbl> <chr>  <dbl>
1      1      1 みかん   200
2      2      2 みかん   200
3      3      2 りんご   300

6 (参考)文字列の操作

{stringr}パッケージによる文字列操作の基本を紹介する

6.1 文字列の結合

複数の文字列の結合はstr_c()で行う

str_c("あいう", "えお")
[1] "あいうえお"

6.2 文字列ベクトルの全結合

文字列ベクトルの全要素を結合してひとつの文字列にまとめるにはstr_flatten()を使う

str_flatten(LETTERS)
[1] "ABCDEFGHIJKLMNOPQRSTUVWXYZ"

6.3 文字列のマッチング

文字列ベクトルの要素のうち、ある文字列(パターン)が入っている要素を取り出したいときはstr_subset()を使う

df[["name"]] |> str_subset("安") # "安"が入っている要素を検索
[1] "安藤" "安田"

文字列ベクトルのなかで、ある文字列(パターン)が入っている要素の位置はstr_which()で取得できる

df[["name"]] |> str_which("川") # "川"が入っている要素の位置
[1] 17 35

7 (参考)Rのチートシート

プログラミング言語などにおいて、基本的な知識をまとめた文書をチートシート(Cheat Sheet)という。

RStudioの公式サイトにもチートシートの一覧がまとめられているが、主要なものはRStudioの[Help]→[Cheatsheets]からも簡単にアクセスできる。

8 参考文献

9 (おまけ)Pythonで同様の作業を行う場合

本講義の演習はRで行うが、参考として、ここまで学んだ内容に対応するPythonのコードを簡単に紹介する。

Pythonでのデータの前処理には、主にpandasというライブラリを使う。Rの{dplyr}や{tidyr}に相当する機能がまとまっている。

import pandas as pd
import numpy as np

9.1 パッケージ

Pythonでのパッケージのインストールはpip install パッケージ名(ターミナル上で実行)、読み込みはimport パッケージ名で行う(Rのinstall.packages()・library()に相当)。

# ターミナル上で実行(Rのinstall.packages()に相当)
# pip install pandas openpyxl

9.2 エクセル(.xlsx)ファイルの読み込み

pd.read_excel()で読み込む(Rのread_excel()に相当)。

df = pd.read_excel("test_scores.xlsx")

9.3 データフレームを概観する

.shapeで行数・列数、.head()で先頭6行、.dtypesで各列のデータ型を確認できる(Rのdim()・head()・str()に相当)。

df.shape       # (40, 5)
df.head()      # 先頭6行
df.dtypes      # 各列のデータ型

9.4 パイプ演算子

pandasには|>のような専用の演算子はないが、メソッドをドット(.)で繋げていくメソッドチェーンが同様の役割を果たす。

df.query("class == 'C'").head()  # Rの df |> subset(class == "C") |> head() に相当

9.5 条件指定による行の取り出し

角括弧に論理値の条件を入れる方法と、.query()メソッドを使う方法がある。

df[df["class"] == "C"]              # Rの df[df["class"] == "C",] に相当
df.query("50 < english < 60")        # Rの filter(df, 50 < english & english < 60) に相当
df[(df["english"] > 90) & (df["japanese"] > 90)]  # 複数条件(Rの if_all(...) に相当)

9.6 列の取り出し

列名を二重の角括弧([[ ]])で指定する。除外は.drop(columns=...)、部分一致検索は.filter(like=...)を使う。

df[["class", "name"]]        # Rの select(df, class, name) に相当
df.drop(columns=["name"])     # Rの select(df, -name) に相当
df.filter(like="an")          # "an"を含む列名(japanese)を取り出す。Rの select(contains("an")) に相当

9.7 列名の変更

.columnsに直接代入する方法と、.rename(columns={...})を使う方法がある(Rのcolnames()・rename()に相当)。

df2 = df[["class", "name"]].copy()
df2.columns = ["class", "NAME"]        # colnames()での変更に相当
df2.rename(columns={"name": "NAME"})   # rename()での変更に相当

9.8 行を並べ替える

.sort_values()を使う(Rのarrange()に相当)。降順にしたい場合はascending=Falseを指定する。

df.sort_values("math")                        # 昇順(Rの arrange(math) に相当)
df.sort_values("english", ascending=False)     # 降順(Rの arrange(desc(english)) に相当)

9.9 新たな列を追加する

角括弧で直接代入する方法と、.assign()メソッドを使う方法がある(Rのdf["新しい列"] <- ...・mutate()に相当)。

df["total_score"] = df["math"] + df["english"] + df["japanese"]

df = df.assign(math_japanese = df["math"] + df["japanese"],
               math_english = df["math"] + df["english"])

9.10 データフレームの連結

pd.concat()を使う。横方向はaxis=1、縦方向はaxis=0を指定する(Rのbind_cols()・bind_rows()に相当)。

A = df[["class", "name"]]
B = df[["math", "english", "japanese"]]
pd.concat([A, B], axis=1)   # 横方向の連結(Rの bind_cols(A, B) に相当)

A2 = df.iloc[0:20]   # 第1~20行
B2 = df.iloc[20:40]  # 第21~40行
pd.concat([A2, B2], axis=0)  # 縦方向の連結(Rの bind_rows(A, B) に相当)

9.11 (参考)データフレームの結合

pd.merge()を使う。how引数で結合方法("inner"・"outer"・"left"・"right")を指定する(Rのinner_join()・full_join()等に相当)。

customers = pd.DataFrame({
    "顧客id": [1, 2, 3],
    "性別": ["男性", "女性", "女性"],
    "年齢": [20, 24, 35],
})
sales = pd.DataFrame({
    "取引id": [1, 2, 3],
    "顧客id": [1, 2, 2],
    "商品": ["みかん", "みかん", "りんご"],
    "金額": [200, 200, 300],
})

pd.merge(customers, sales, on="顧客id", how="outer")  # Rの full_join() に相当
pd.merge(customers, sales, on="顧客id", how="inner")   # Rの inner_join() に相当

9.12 (参考)データフレームの展開

ワイド形式への変換は.pivot()、ロング形式への変換は.melt()を使う(Rのpivot_wider()・pivot_longer()に相当)。

# ワイド形式(Rの pivot_wider() に相当)
sales_wide = sales.pivot(index="顧客id", columns="商品", values="金額")

# ロング形式に戻す(Rの pivot_longer() に相当)
sales_long = (
    sales_wide.reset_index()
    .melt(id_vars="顧客id", var_name="商品", value_name="金額")
    .dropna()
)

9.13 (参考)文字列の操作

文字列の結合は+や"".join()、文字列ベクトル(Series)に対するパターンマッチには.strアクセサを使う(Rのstr_c()・str_flatten()・str_subset()・str_which()に相当)。

"あいう" + "えお"          # 文字列の結合(Rの str_c() に相当)
"".join(["A", "B", "C"])   # 全結合(Rの str_flatten() に相当)

names = df["name"]
names[names.str.contains("安")]        # "安"が入っている要素を検索(Rの str_subset() に相当)
np.where(names.str.contains("川"))[0]  # "川"が入っている要素の位置(Rの str_which() に相当)