c関数で生成されるアトミックベクトルは、全て同じ型であることが前提でした。
#異なる型のものを入れると強制的に同一の型へと変換
a = c(13,TRUE,"hello")
print(a)
## [1] "13" "TRUE" "hello"
# aの型は「character」
typeof(a)
## [1] "character"
list関数でつくられるリストであれば、異なる型を1つの変数にグループとしてまとめることができます。
a = list(13,TRUE,"hello")
print(a)
## [[1]]
## [1] 13
##
## [[2]]
## [1] TRUE
##
## [[3]]
## [1] "hello"
# aの型とクラスはともに「リスト」
typeof(a); class(a);
## [1] "list"
## [1] "list"
個々の要素には、a[[n]]
でアクセスできます。
a[[1]]; a[[2]]; a[[3]]
## [1] 13
## [1] TRUE
## [1] "hello"
これらの要素は、(今回は)全てアトミックベクトルなので、 typeof関数で型にアクセスできます。
typeof(a[[1]]);typeof(a[[2]]);typeof(a[[3]])
## [1] "double"
## [1] "logical"
## [1] "character"
実際には、リストの要素はアトミックベクトルとは限りません。
次で、そのような例を確認していきます。
(復習)リストのn番目の要素はリスト[[n]]
で表します。
a = list(13,TRUE,"hello")
a[[1]];a[[2]];a[[3]]
## [1] 13
## [1] TRUE
## [1] "hello"
リストのn番目の要素がベクトルの場合、
そのベクトルのm番目の要素は[[n]][m]
で表します。
a[[1]][1];a[[2]][1];a[[3]][1]
## [1] 13
## [1] TRUE
## [1] "hello"
リスト[n]
は、リスト[[n]]
を要素として持つ「サイズ1」のリストを返します。
print(a[1]);print(a[2]);print(a[3])
## [[1]]
## [1] 13
## [[1]]
## [1] TRUE
## [[1]]
## [1] "hello"
ややこしいですが、以下の違いに注意してください。
a = list(13,TRUE,"hello")
typeof(a[[1]]); typeof(a[1]); typeof(a[1][[1]])
## [1] "double"
## [1] "list"
## [1] "double"
もう少し、例題を通して確認していきましょう。
以下は、リストの要素が全てアトミックベクトルの例です。
a = list(11:13,c(T,F,F,F),"good-bye")
print(a)
## [[1]]
## [1] 11 12 13
##
## [[2]]
## [1] TRUE FALSE FALSE FALSE
##
## [[3]]
## [1] "good-bye"
各ベクトルの要素は以下のように参照できます。
a[[1]][2];a[[2]][4]
## [1] 12
## [1] FALSE
# aの三番目の要素はアトミックベクトル
print(a[[3]])
## [1] "good-bye"
# a[3]は、a[[3]]を要素に持つサイズ1のリストとなります。(復習)
a3 = a[3]; print(a3)
## [[1]]
## [1] "good-bye"
typeof(a3);length(a3)
## [1] "list"
## [1] 1
# a[1]は、11:13(a[[1]])を要素に持つサイズ1のリスト
a1 = a[1]; print(a1)
## [[1]]
## [1] 11 12 13
typeof(a1);length(a1)
## [1] "list"
## [1] 1
リストはリストを要素に加えることができます。
a = list(list(T,c("hello","good-bye")),c(T,F,F,F),101:200)
print(a)
## [[1]]
## [[1]][[1]]
## [1] TRUE
##
## [[1]][[2]]
## [1] "hello" "good-bye"
##
##
## [[2]]
## [1] TRUE FALSE FALSE FALSE
##
## [[3]]
## [1] 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118
## [19] 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
## [37] 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154
## [55] 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
## [73] 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190
## [91] 191 192 193 194 195 196 197 198 199 200
リストのリストは[[p]][[q]]..
という形で掘っていきます。
print(a[[1]]) #aの第一要素もリスト構造
## [[1]]
## [1] TRUE
##
## [[2]]
## [1] "hello" "good-bye"
# aの第1要素の第2要素はアトミックベクトル
a[[1]][[2]]
## [1] "hello" "good-bye"
# a(リスト)の第1要素(リスト)の第2要素(ベクトル)の第2要素は"good-bye"
print(a[[1]][[2]][2])
## [1] "good-bye"
くどいですが、再び、リストとアトミックベクトルの違いを復習します。
a = c("hello","good-bye")
b = list("hello","good-bye")
# "hello"の参照は?
a[1]; b[[1]][1]
## [1] "hello"
## [1] "hello"
# "good-bye"の参照は?
a[2]; b[[2]][1]
## [1] "good-bye"
## [1] "good-bye"
リストの各要素には名前属性をつけることができます。
chubu = list(name = c("AICHI","GIFU","MIE","SHIZUOKA"), #県の名前
pop = c(716,211,186,379), #人口
order = c(4,17,22,10), #人口順位
capital = c("nagoya","gifu","mie","shizuoka"), #県庁所在地
shinkansen = c(T,T,F,T)) #新幹線通過の有無
このリストの特定のベクトルにアクセスするのに、名前属性を使うことができます。この記法をドル記法($記法)と呼びます。
chubu$pop #chubu[[2]]と同じ
## [1] 716 211 186 379
chubu$capital #chubu[[4]]と同じ
## [1] "nagoya" "gifu" "mie" "shizuoka"
さらに、個々の属性の要素には以下のように参照できます。
#愛知県の県庁所在地は、
chubu$capital[1]
## [1] "nagoya"
#岐阜県の人口順位は
chubu$order[2]
## [1] 17
#三重県の新幹線通過の有無は、
chubu$shinkansen[3]
## [1] FALSE
#静岡県の人口は
chubu$pop[4]
## [1] 379
先週の授業の例:n個のサイコロを振って、総和を得るプログラムです。
roll.n = function(n){
dice = 1:6
dice2 = sample(dice, size=n, replace = TRUE)
# dice2 = sample(dice,n,T) #こちらでも可
sum(dice2)
}
# サイコロを3回振った総和の1000サンプルのベクトルを出力
sample = replicate(1000,roll.n(3))
print(head(sample,100))
## [1] 13 11 11 12 15 11 7 9 11 9 13 10 10 6 8 13 15 9 14 13 7 4 11 5 15
## [26] 9 9 10 12 12 7 10 12 12 13 10 12 8 16 15 13 9 6 14 10 8 9 11 12 13
## [51] 5 8 13 12 13 15 15 11 6 16 14 8 13 11 7 16 10 8 9 18 17 10 14 13 16
## [76] 9 7 14 14 12 10 13 14 8 12 13 7 10 10 9 8 12 14 11 16 11 16 13 9 13
# ヒストグラムを計算し、Plotsパネルにグラフを出力します。
hi = hist(sample,breaks=1:20)
hist関数の返り値であるhiは、 実はhistogramクラスのリストです。
typeof(hi);class(hi)
## [1] "list"
## [1] "histogram"
実際、右上のEnvironmentパネルで「hi」を開くと、
6つの名前属性($breaks
,$counts
,$density
,$mids
,$xname
,$equidist
)の存在が確認できます。
# 総和サンプルの一覧
#リストなので二重角括弧を使うこともできます。
hi$breaks; hi[[1]]
## [1] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
## [1] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
#個々のビンに含まれるサンプル数(例です)
hi$counts; hi[[2]]
## [1] 0 1 9 27 34 72 106 124 103 130 123 106 68 42 35 15 5 0 0
## [1] 0 1 9 27 34 72 106 124 103 130 123 106 68 42 35 15 5 0 0
簡単なベクトルを例に、論理値による条件に基づく抽出の方法を見ていきます。
まず1から20までの整数列をつくります。
n = 1:20; print(n)
## [1] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
同じサイズの論理値のベクトルを使うと特定の要素だけ取り出すことができます。
# 奇数部分だけを取り出す例
n[c(T,F,T,F,T,F,T,F,T,F,T,F,T,F,T,F,T,F,T,F)]
## [1] 1 3 5 7 9 11 13 15 17 19
n[c(T,F)] # 同様(リサイクル規則を使う)
## [1] 1 3 5 7 9 11 13 15 17 19
# 3要素ごとに値を取り出す
n[c(T,F,F)] #リサイクル規則
## [1] 1 4 7 10 13 16 19
c(T,F,..)
というベクトルは以下の方法でも作れます。
n = 1:20; n %% 2 == 1
## [1] TRUE FALSE TRUE FALSE TRUE FALSE TRUE FALSE TRUE FALSE TRUE FALSE
## [13] TRUE FALSE TRUE FALSE TRUE FALSE TRUE FALSE
これを使えば、よりスマートに、特定の要素群を取り出すことが可能です。
n = 1:20;
n[n %% 2 == 1]
## [1] 1 3 5 7 9 11 13 15 17 19
n = 1:50;
n[n %% 3 == 1]
## [1] 1 4 7 10 13 16 19 22 25 28 31 34 37 40 43 46 49
1000から100個のサンプルをランダムに抽出し、
そのうち10で割り切れるもののみを取り出してみます。
# 1000から100サンプルを取得
n = sample(1000,100)
print(head(n,50)) #最初の50だけを取り出す
## [1] 570 261 187 693 20 118 763 487 301 725 910 306 239 821 64 319 669 715 723
## [20] 481 312 824 739 285 398 25 29 349 188 662 504 737 762 803 450 66 226 454
## [39] 292 598 954 732 184 881 290 348 434 353 277 699
# 10で割り切れるもののみを羅列
n[n %% 10 == 0]
## [1] 570 20 910 450 290 660 110 990 60 70 560
#一応、こうなっています。
n %% 10 == 0
## [1] TRUE FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE TRUE FALSE
## [13] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## [25] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE
## [37] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE
## [49] FALSE FALSE FALSE FALSE FALSE TRUE FALSE FALSE TRUE FALSE FALSE FALSE
## [61] FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE
## [73] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## [85] FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE TRUE FALSE FALSE FALSE
## [97] FALSE FALSE TRUE FALSE
ここからリストに戻ります。
まずは、3回サイコロを振った総和のヒストグラムを作成します。
roll.n = function(n){
dice = 1:6
dice2 = sample(dice,n,T)
sum(dice2)
}
sample = replicate(1000,roll.n(3))
#breaksは区切りのライン
#2.5~3.5, 3.5~4.5, 4.5~5.5, ...17.5~18.5をサンプリング
hi = hist(sample,breaks=2.5:18.5)
hi$XXX
はベクトルのため、同じ探索方法が適用できます。
# hi$midsは、各ビンの中央のサンプル値(最小値は3となります)
hi$mids
## [1] 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
# それぞれのビンの頻度
hi$counts
## [1] 3 10 22 47 78 108 113 138 116 106 102 67 43 28 14 5
# 総和10のサンプル数は?
# (後述の論理値サーチを参照)
hi$counts[hi$mids==10]
## [1] 138
# 総和10以上の各サンプル数は?
hi$counts[hi$mids>=10]
## [1] 138 116 106 102 67 43 28 14 5
# 総和10以上のサンプル数の総和は、
sum(hi$counts[hi$mids>=10]) #欠損値があるとNAになります
## [1] 619
# 総和10以上の確率密度は?
sum(hi$counts[hi$mids>=10])/sum(hi$counts)
## [1] 0.619
まずは、(LIST.3)で作った中部地方のリストの生成関数を、
list
からdata.frame
に変更します。
chubu = data.frame(name = c("AICHI","GIFU","MIE","SHIZUOKA"), #県の名前
pop = c(716,211,186,379), #人口
order = c(4,17,22,10), #人口順位
capital = c("nagoya","gifu","mie","shizuoka"), #県庁所在地
shinkansen = c(T,T,F,T)) #新幹線通過の有無
chubuの中身を見ると、データが綺麗に整列されています。
print(chubu)
## name pop order capital shinkansen
## 1 AICHI 716 4 nagoya TRUE
## 2 GIFU 211 17 gifu TRUE
## 3 MIE 186 22 mie FALSE
## 4 SHIZUOKA 379 10 shizuoka TRUE
型はリストのまま、クラスがdata.frameとなることに注意してください。
typeof(chubu);class(chubu)
## [1] "list"
## [1] "data.frame"
DF[n]
は、n
番目の要素をデータフレーム形式で返します。
chubu[2]; chubu[4]
## pop
## 1 716
## 2 211
## 3 186
## 4 379
## capital
## 1 nagoya
## 2 gifu
## 3 mie
## 4 shizuoka
データフレームは、全ての要素がアトミックベクトルで、
かつ、それらの要素数が同一でないとエラーが返されます。
例えば、以下の場合、shinkansenの要素数のみが3で揃わないためエラーとなります。
chubu = data.frame(name = c("AICHI","GIFU","MIE","SHIZUOKA"), #県の名前
pop = c(716,211,186,379), #人口
order = c(4,17,22,10), #人口順位
capital = c("nagoya","gifu","mie","shizuoka"), #県庁所在地
shinkansen = c(T,T,F)) #新幹線通過の有無
## Error in data.frame(name = c("AICHI", "GIFU", "MIE", "SHIZUOKA"), pop = c(716, : arguments imply differing number of rows: 4, 3
DF[i,j]
)データフレームにおいて、個々の要素を直感的に指定する記法として行列添字(ij)記法があります。
chubu = data.frame(name = c("AICHI","GIFU","MIE","SHIZUOKA"), #県の名前
pop = c(716,211,186,379), #人口
order = c(4,17,22,10), #人口順位
capital = c("nagoya","gifu","mie","shizuoka"), #県庁所在地
shinkansen = c(T,T,F,T)) #新幹線通過の有無
print(chubu)
## name pop order capital shinkansen
## 1 AICHI 716 4 nagoya TRUE
## 2 GIFU 211 17 gifu TRUE
## 3 MIE 186 22 mie FALSE
## 4 SHIZUOKA 379 10 shizuoka TRUE
DF[i,j]
で、データフレームを行列と見立てた時の
i
行目j
列目の要素を取り出すことができます。
# 三重県の人口順位(3行3列目)
chubu[3,3] #対応するベクトル要素を返します。
## [1] 22
# 名前属性を使った$記法も、リスト要素の記法も使えます。
chubu$order[3]; chubu[[3]][3]
## [1] 22
## [1] 22
行列添字記法の拡張として、
列(j
)の要素数が複数の場合、サブのデータフレームを返します。
chubu[3,c(1,3)] #三重の県名と人口順位(3行の1列目と3列目)
## name order
## 3 MIE 22
chubu[1:3,c(1,5)] #静岡以外の名前と新幹線情報
## name shinkansen
## 1 AICHI TRUE
## 2 GIFU TRUE
## 3 MIE FALSE
chubu[-4,c(1,5)] #上と同じです。(4行目を除外)
## name shinkansen
## 1 AICHI TRUE
## 2 GIFU TRUE
## 3 MIE FALSE
chubu[3,] #三重県の全て
## name pop order capital shinkansen
## 3 MIE 186 22 mie FALSE
他方で、j
の要素数が「1つ」の場合は、ベクトルが返ります。
chubu[,1] #全ての県
## [1] "AICHI" "GIFU" "MIE" "SHIZUOKA"
chubu[,4] #全ての県の県庁所在地
## [1] "nagoya" "gifu" "mie" "shizuoka"
!!
1つの行(i=1
)を選択すると,データフレームを返す一方で、<
!!
1つの列(j=1
)を選択すると,アトミックベクトルを返すことに注意
データフレームの中の特定の一行を抽出しても、
それらの型が同一とは限らないので、
そもそもアトミックベクトルにはなりえません。
chubu[1,] #愛知県(1行目)の全てのデータ
## name pop order capital shinkansen
## 1 AICHI 716 4 nagoya TRUE
chubu[2,] #岐阜県(2行目)のデータ
## name pop order capital shinkansen
## 2 GIFU 211 17 gifu TRUE
順番を入れ替えることもできます。
# 2行目、1行目、3行目、4行目の順に並べ替え
chubu[c(2,1,3,4),]
## name pop order capital shinkansen
## 2 GIFU 211 17 gifu TRUE
## 1 AICHI 716 4 nagoya TRUE
## 3 MIE 186 22 mie FALSE
## 4 SHIZUOKA 379 10 shizuoka TRUE
# 列指定で順番の入れ替え
# 列のサイズが1なので、やはりベクトルが返ります。
chubu[c(2,1,3,4),2] #人口を愛知・岐阜・三重・静岡の順で
## [1] 211 716 186 379
# 行も列も順番の入れ替え
# 列のサイズが2なので、データフレームで返します。
chubu[c(2,1,3,4),c(4,1)]
## capital name
## 2 gifu GIFU
## 1 nagoya AICHI
## 3 mie MIE
## 4 shizuoka SHIZUOKA
再び、同じデータフレームを生成するところからはじめましょう。
chubu = data.frame(name = c("AICHI","GIFU","MIE","SHIZUOKA"), #県の名前
pop = c(716,211,186,379), #人口
order = c(4,17,22,10), #人口順位
capital = c("nagoya","gifu","mie","shizuoka"), #県庁所在地
shinkansen = c(T,T,F,T)) #新幹線通過の有無
print(chubu)
## name pop order capital shinkansen
## 1 AICHI 716 4 nagoya TRUE
## 2 GIFU 211 17 gifu TRUE
## 3 MIE 186 22 mie FALSE
## 4 SHIZUOKA 379 10 shizuoka TRUE
列の名前属性を、行列添字記法のj
(列)に直接に指定することができます。
j
のサイズが複数の場合、データフレームとして返ります。
chubu[1,c("name","pop")] #愛知県の名前と人口
## name pop
## 1 AICHI 716
chubu[1,c("pop","name")] #名前を入れ替えると順番も変わる
## pop name
## 1 716 AICHI
chubu[,c("name","pop")] #全ての県の名前と人口
## name pop
## 1 AICHI 716
## 2 GIFU 211
## 3 MIE 186
## 4 SHIZUOKA 379
# jのサイズが1の場合、ベクトルが返ることに注意
chubu[,c("pop")] #全ての県の人口
## [1] 716 211 186 379
データフレームはリストの一種ですので、[LIST.3]で紹介した$記法を適用できます。
chubu$capital #全ての県の県庁所在地、ベクトルを返します。
## [1] "nagoya" "gifu" "mie" "shizuoka"
chubu$shinkansen #全ての県の新幹線状況(ベクトル)
## [1] TRUE TRUE FALSE TRUE
chubu$pop #全ての県の人口(ベクトル)
## [1] 716 211 186 379
以下の3つの文は全て同じ値を返します。
chubu$capital; chubu[,c("capital")]; chubu[,4]
## [1] "nagoya" "gifu" "mie" "shizuoka"
## [1] "nagoya" "gifu" "mie" "shizuoka"
## [1] "nagoya" "gifu" "mie" "shizuoka"
要するに、$記法における$はデータフレームの列(j)に相当すると覚えてください。(iがメンバ、jが変数(属性)、またはiがレコード、jがフィールドみたいな覚えかたでもいい)
$記法の返り値はアトミックベクトルのため、 一般的な統計関数の引数にそのまま入ることができます。
sum(chubu$pop) #人口の総和
## [1] 1492
mean(chubu$pop) #人口の平均
## [1] 373
数学関数の引数は、アトミックベクトルであることが通常です。
以下のケースではエラーとなることに注意してください。
mean(chubu[2]) #エラー(chubu[2]はベクトルではなくリスト!!)
## Warning in mean.default(chubu[2]): argument is not numeric or logical:
## returning NA
## [1] NA
mean(list(c(716,211,186,379))) #同じくエラー
## Warning in mean.default(list(c(716, 211, 186, 379))): argument is not numeric
## or logical: returning NA
## [1] NA
二重角括弧でベクトルに変換すれば、一般的な関数の引数にとれます。
mean(chubu[[2]])
## [1] 373
再び、同じデータフレームを使って説明していきます。
chubu = data.frame(name = c("AICHI","GIFU","MIE","SHIZUOKA"), #県の名前
pop = c(716,211,186,379), #人口
order = c(4,17,22,10), #人口順位
capital = c("nagoya","gifu","mie","shizuoka"), #県庁所在地
shinkansen = c(T,T,F,T)) #新幹線通過の有無
print(chubu)
## name pop order capital shinkansen
## 1 AICHI 716 4 nagoya TRUE
## 2 GIFU 211 17 gifu TRUE
## 3 MIE 186 22 mie FALSE
## 4 SHIZUOKA 379 10 shizuoka TRUE
(LIST.5)で扱った論理値による条件付き抽出は、データフレームで大活躍します。
chubu[1,c(T,T,F,F,F)] #愛知県の名前と人口のみ
## name pop
## 1 AICHI 716
chubu[c(T,F,F,F),c(1,2)] #上に同じです。
## name pop
## 1 AICHI 716
# 人口順位のみをベクトルとして取り出す
chubu$order; #chubu[,3]と同じ
## [1] 4 17 22 10
# 人口順位10番以内の県をサーチ(真偽値ベクトルが返ります)
chubu$order<=10
## [1] TRUE FALSE FALSE TRUE
# 条件を満たす行をデータフレームとして取り出す
chubu[chubu$order<=10,]
## name pop order capital shinkansen
## 1 AICHI 716 4 nagoya TRUE
## 4 SHIZUOKA 379 10 shizuoka TRUE
同様に、特定の条件を満たす県をベクトルで取り出す例をみていきます。
# 以下は、全て人口トップ10の県を取り出す例
chubu[chubu$order<=10,1]
chubu[[1]][chubu$order<=10]
chubu$name[chubu$order<=10]
## [1] "AICHI" "SHIZUOKA"
## [1] "AICHI" "SHIZUOKA"
## [1] "AICHI" "SHIZUOKA"
# 新幹線の通らない県の人口順位
chubu$order[chubu$shinkansen==F]
## [1] 22
# 新幹線の通る県の人口順位
chubu$order[chubu$shinkansen==T]
## [1] 4 17 10
以下から、メジャーリーグの各年の成績データが入手できます。
-
https://baseballsavant.mlb.com/leaderboard
以下は、上記から2021〜2022年の成績をCSVとしてDLしたものです。
CSVファイルは以下のようなフォーマットになっています。
一行目に名前属性、二行目以降に各データが並んでいます。
それぞれの名前属性は、以下のデータと対応します(野球知らない人ごめん)。
last_name(ラストネーム),first_name(ファーストネーム), player_id(id),year(成績年),player_age(年齢), ab(打席数),hit(安打数),single(単打),double(二塁打),triple(三塁打), home_run(本塁打),strikeout(三振),walk(四球), k_percent(三振率),bb_parcent(四球率), batting_avg(打率),on_base_percent(出塁率),on_base_plus_slg(OPS) wOBA(打撃貢献指標), X(?)
作業ディレクトリを指定します。(以下は僕の環境の例です。)
setwd("/Users/kenrikodaka/Dropbox/DocClass/_2023/B3_情報処理応用")
メニューからも指定できます(Session → ChooseDirectory)
setwdをしておくと、データを読み込むときに楽になります。
以下のようにカレントディレクトリからの相対パスでデータを指定できます。
read.csv(dat.csv)
を用いると、csvデータをデータフレームとして読み込むことができます。
datb = read.csv("_dat/mlb.b.2021-22.csv") #打撃成績
datp = read.csv("_dat/mlb.p.2021-22.csv") #投手成績
read.csv
関数の引数で、直接URL(拡張子csv)を指定することで、インターネット上で公開されているデータフレームに直接にアクセスすることができます。
# バッターの成績:2021 - 2022
url_b = "http://lab.kenrikodaka.com/_download/class/2023_AppliedMedia/mlb.b.2021-22.csv"
# ピッチャーの成績:2021 - 2022
url_p = "http://lab.kenrikodaka.com/_download/class/2023_AppliedMedia/mlb.p.2021-22.csv"
datb = read.csv(url_b) #打撃成績
datp = read.csv(url_p) #投手成績
datb
、datp
の中身については、次章から確認していきます。
まず、データフレームの概要を見るための関数をいくつか紹介します。
まずは、最初(head
関数)と最後(tail
関数)の指定行を抽出する方法から。
# 打撃成績データの最初の6行をちょっと出し
head(datb)
## last_name first_name player_id year player_age ab pa hit single double
## 1 Frazier Adam 624428 2021 29 577 639 176 130 36
## 2 Altuve Jose 514888 2021 31 601 678 167 103 32
## 3 Gallo Joey 608336 2021 27 498 616 99 47 13
## 4 Sano Miguel 593934 2021 28 470 532 105 51 24
## 5 Kiner-Falefa Isiah 643396 2021 26 635 677 172 136 25
## 6 Edman Tommy 669242 2021 26 641 691 168 113 41
## triple home_run strikeout walk k_percent bb_percent batting_avg slg_percent
## 1 5 5 69 48 10.8 7.5 0.305 0.411
## 2 1 31 91 66 13.4 9.7 0.278 0.489
## 3 1 38 213 111 34.6 18.0 0.199 0.458
## 4 0 30 183 59 34.4 11.1 0.223 0.466
## 5 3 8 90 28 13.3 4.1 0.271 0.357
## 6 3 11 95 38 13.7 5.5 0.262 0.387
## on_base_percent on_base_plus_slg woba X
## 1 0.368 0.779 0.341 NA
## 2 0.350 0.839 0.357 NA
## 3 0.351 0.809 0.348 NA
## 4 0.312 0.778 0.332 NA
## 5 0.312 0.669 0.293 NA
## 6 0.308 0.695 0.301 NA
head(datb,10) #行数を指定
## last_name first_name player_id year player_age ab pa hit single double
## 1 Frazier Adam 624428 2021 29 577 639 176 130 36
## 2 Altuve Jose 514888 2021 31 601 678 167 103 32
## 3 Gallo Joey 608336 2021 27 498 616 99 47 13
## 4 Sano Miguel 593934 2021 28 470 532 105 51 24
## 5 Kiner-Falefa Isiah 643396 2021 26 635 677 172 136 25
## 6 Edman Tommy 669242 2021 26 641 691 168 113 41
## 7 Happ Ian 664023 2021 26 465 535 105 59 20
## 8 Merrifield Whit 593160 2021 32 664 720 184 129 42
## 9 Fletcher David 664058 2021 27 626 665 164 132 27
## 10 O'Neill Tyler 641933 2021 26 482 537 138 76 26
## triple home_run strikeout walk k_percent bb_percent batting_avg slg_percent
## 1 5 5 69 48 10.8 7.5 0.305 0.411
## 2 1 31 91 66 13.4 9.7 0.278 0.489
## 3 1 38 213 111 34.6 18.0 0.199 0.458
## 4 0 30 183 59 34.4 11.1 0.223 0.466
## 5 3 8 90 28 13.3 4.1 0.271 0.357
## 6 3 11 95 38 13.7 5.5 0.262 0.387
## 7 1 25 156 62 29.2 11.6 0.226 0.434
## 8 3 10 103 40 14.3 5.6 0.277 0.395
## 9 3 2 60 31 9.0 4.7 0.262 0.324
## 10 2 34 168 38 31.3 7.1 0.286 0.560
## on_base_percent on_base_plus_slg woba X
## 1 0.368 0.779 0.341 NA
## 2 0.350 0.839 0.357 NA
## 3 0.351 0.809 0.348 NA
## 4 0.312 0.778 0.332 NA
## 5 0.312 0.669 0.293 NA
## 6 0.308 0.695 0.301 NA
## 7 0.323 0.757 0.328 NA
## 8 0.317 0.712 0.306 NA
## 9 0.297 0.621 0.273 NA
## 10 0.352 0.912 0.384 NA
tail(datb) #末尾(6行)を確認
## last_name first_name player_id year player_age ab pa hit single double
## 257 Seager Corey 608369 2022 28 593 663 145 87 24
## 258 Abreu Jose 547989 2022 35 601 679 183 128 40
## 259 Bichette Bo 666182 2022 24 652 697 189 121 43
## 260 Lindor Francisco 596019 2022 28 630 706 170 114 25
## 261 Arraez Luis 650333 2022 25 547 603 173 133 31
## 262 Kwan Steven 680757 2022 24 563 638 168 130 25
## triple home_run strikeout walk k_percent bb_percent batting_avg slg_percent
## 257 1 33 103 58 15.5 8.7 0.245 0.455
## 258 0 15 110 62 16.2 9.1 0.304 0.446
## 259 1 24 155 41 22.2 5.9 0.290 0.469
## 260 5 26 133 59 18.8 8.4 0.270 0.449
## 261 1 8 43 50 7.1 8.3 0.316 0.420
## 262 7 6 60 62 9.4 9.7 0.298 0.400
## on_base_percent on_base_plus_slg woba X
## 257 0.317 0.772 0.331 NA
## 258 0.378 0.824 0.361 NA
## 259 0.333 0.802 0.347 NA
## 260 0.339 0.788 0.342 NA
## 261 0.375 0.795 0.350 NA
## 262 0.373 0.773 0.341 NA
tail(datb,1) #最後の行のみを取り出す
## last_name first_name player_id year player_age ab pa hit single double
## 262 Kwan Steven 680757 2022 24 563 638 168 130 25
## triple home_run strikeout walk k_percent bb_percent batting_avg slg_percent
## 262 7 6 60 62 9.4 9.7 0.298 0.4
## on_base_percent on_base_plus_slg woba X
## 262 0.373 0.773 0.341 NA
全体の構造を見るにはstr関数を使います。
str(datb)
## 'data.frame': 262 obs. of 22 variables:
## $ last_name : chr "Frazier" "Altuve" "Gallo" "Sano" ...
## $ first_name : chr " Adam" " Jose" " Joey" " Miguel" ...
## $ player_id : int 624428 514888 608336 593934 643396 669242 664023 593160 664058 641933 ...
## $ year : int 2021 2021 2021 2021 2021 2021 2021 2021 2021 2021 ...
## $ player_age : int 29 31 27 28 26 26 26 32 27 26 ...
## $ ab : int 577 601 498 470 635 641 465 664 626 482 ...
## $ pa : int 639 678 616 532 677 691 535 720 665 537 ...
## $ hit : int 176 167 99 105 172 168 105 184 164 138 ...
## $ single : int 130 103 47 51 136 113 59 129 132 76 ...
## $ double : int 36 32 13 24 25 41 20 42 27 26 ...
## $ triple : int 5 1 1 0 3 3 1 3 3 2 ...
## $ home_run : int 5 31 38 30 8 11 25 10 2 34 ...
## $ strikeout : int 69 91 213 183 90 95 156 103 60 168 ...
## $ walk : int 48 66 111 59 28 38 62 40 31 38 ...
## $ k_percent : num 10.8 13.4 34.6 34.4 13.3 13.7 29.2 14.3 9 31.3 ...
## $ bb_percent : num 7.5 9.7 18 11.1 4.1 5.5 11.6 5.6 4.7 7.1 ...
## $ batting_avg : num 0.305 0.278 0.199 0.223 0.271 0.262 0.226 0.277 0.262 0.286 ...
## $ slg_percent : num 0.411 0.489 0.458 0.466 0.357 0.387 0.434 0.395 0.324 0.56 ...
## $ on_base_percent : num 0.368 0.35 0.351 0.312 0.312 0.308 0.323 0.317 0.297 0.352 ...
## $ on_base_plus_slg: num 0.779 0.839 0.809 0.778 0.669 0.695 0.757 0.712 0.621 0.912 ...
## $ woba : num 0.341 0.357 0.348 0.332 0.293 0.301 0.328 0.306 0.273 0.384 ...
## $ X : logi NA NA NA NA NA NA ...
str(datp)
## 'data.frame': 204 obs. of 24 variables:
## $ last_name : chr "Wainwright" "Greinke" "Hill" "Morton" ...
## $ first_name : chr " Adam" " Zack" " Rich" " Charlie" ...
## $ player_id : int 425794 425844 448179 450203 452657 453286 457918 458681 489119 501985 ...
## $ year : int 2021 2021 2021 2021 2021 2021 2021 2021 2021 2021 ...
## $ player_age : int 39 37 41 37 37 36 38 34 34 33 ...
## $ p_game : int 32 30 32 33 28 30 30 28 28 28 ...
## $ p_formatted_ip : num 206 171 158 185 141 ...
## $ pa : int 828 697 661 756 627 693 671 641 690 669 ...
## $ ab : int 765 652 582 669 558 644 615 588 627 619 ...
## $ hit : int 168 164 137 136 159 119 177 123 166 156 ...
## $ single : int 111 105 83 100 101 73 106 84 112 91 ...
## $ double : int 34 29 32 17 33 21 35 20 35 35 ...
## $ triple : int 2 0 1 3 0 2 6 1 2 4 ...
## $ home_run : int 21 30 21 16 25 23 30 18 17 26 ...
## $ strikeout : int 174 120 150 216 91 236 122 176 125 149 ...
## $ walk : int 50 36 55 58 55 36 48 45 50 41 ...
## $ k_percent : num 21 17.2 22.7 28.6 14.5 34.1 18.2 27.5 18.1 22.3 ...
## $ bb_percent : num 6 5.2 8.3 7.7 8.8 5.2 7.2 7 7.2 6.1 ...
## $ batting_avg : num 0.22 0.252 0.235 0.203 0.285 0.185 0.288 0.209 0.265 0.252 ...
## $ slg_percent : num 0.352 0.434 0.402 0.309 0.478 0.331 0.511 0.338 0.408 0.447 ...
## $ on_base_percent : num 0.275 0.291 0.316 0.281 0.349 0.239 0.341 0.267 0.321 0.303 ...
## $ on_base_plus_slg: num 0.627 0.725 0.718 0.59 0.827 0.57 0.852 0.605 0.729 0.75 ...
## $ woba : num 0.273 0.309 0.313 0.264 0.352 0.248 0.361 0.264 0.317 0.321 ...
## $ X : logi NA NA NA NA NA NA ...
データフレームのサイズを確認するには
# dim:行数と列数, nrow:行数, ncol:列数
dim(datb); nrow(datb); ncol(datb)
## [1] 262 22
## [1] 262
## [1] 22
dim(datp); nrow(datp); ncol(datp)
## [1] 204 24
## [1] 204
## [1] 24
それでは、いよいよ特定の条件を満たす選手を見つけていきましょう。
# 打率が3割を超えている選手を列挙
datb$last_name[datb$batting_avg>0.3]
## [1] "Frazier" "Harper" "Marte" "Anderson" "Soto"
## [6] "Castellanos" "Brantley Jr." "Riley" "Reynolds" "Gurriel"
## [11] "Turner" "Guerrero Jr." "Freeman" "Alvarez" "Benintendi"
## [16] "Judge" "Goldschmidt" "Bogaerts" "Lowe" "McNeil"
## [21] "Abreu" "Arraez"
# ホームランを40本以上売っている選手の名前を列挙
datb$last_name[datb$home_run>=40]
## [1] "Tatis Jr." "Ohtani" "Perez" "Guerrero Jr." "Semien"
## [6] "Schwarber" "Judge" "Alonso"
# ホームランを40本以上売っている選手の成績を抽出(1〜5列のみ)
# (データフレームとして出力されます)
datb[datb$home_run>=40,1:5]
## last_name first_name player_id year player_age
## 15 Tatis Jr. Fernando 665487 2021 22
## 27 Ohtani Shohei 660271 2021 26
## 110 Perez Salvador 521692 2021 31
## 125 Guerrero Jr. Vladimir 665489 2021 22
## 131 Semien Marcus 543760 2021 30
## 170 Schwarber Kyle 656941 2022 29
## 189 Judge Aaron 592450 2022 30
## 252 Alonso Pete 624413 2022 27
# ホームランを40本以上の記録を列挙
# (アトミックベクトルでの出力)
datb$home_run[datb$home_run>=40]
## [1] 42 46 48 48 45 46 62 40
# ホームランを40本以上の選手の名前・成績年・本塁打数を
# データフレームで抽出
datb[datb$home_run>=40,c("last_name","year","home_run")]
## last_name year home_run
## 15 Tatis Jr. 2021 42
## 27 Ohtani 2021 46
## 110 Perez 2021 48
## 125 Guerrero Jr. 2021 48
## 131 Semien 2021 45
## 170 Schwarber 2022 46
## 189 Judge 2022 62
## 252 Alonso 2022 40
A & B
(AかつBが真のとき真)A | B
(AまたはBが真のとき真)xor(A,B)
(AとBのうち1つだけが真のとき真)!A
(Aが偽のとき偽)any(A,B,C,...)
(いずれかが真のとき真)all(A,B,C,...)
(いずれも真のとき真)# 再び、打率が3割を超えている選手を列挙
datb$last_name[datb$batting_avg>0.3]
## [1] "Frazier" "Harper" "Marte" "Anderson" "Soto"
## [6] "Castellanos" "Brantley Jr." "Riley" "Reynolds" "Gurriel"
## [11] "Turner" "Guerrero Jr." "Freeman" "Alvarez" "Benintendi"
## [16] "Judge" "Goldschmidt" "Bogaerts" "Lowe" "McNeil"
## [21] "Abreu" "Arraez"
# 2021年に限定(&は論理和)
bat2021 = datb$last_name[datb$year==2021 & datb$batting_avg>0.3]
print(bat2021)
## [1] "Frazier" "Harper" "Marte" "Anderson" "Soto"
## [6] "Castellanos" "Brantley Jr." "Riley" "Reynolds" "Gurriel"
## [11] "Turner" "Guerrero Jr."
# 2022年に限定
bat2022 = datb$last_name[datb$year==2022 & datb$batting_avg>0.3]
print(bat2022)
## [1] "Freeman" "Alvarez" "Benintendi" "Judge" "Goldschmidt"
## [6] "Bogaerts" "Lowe" "McNeil" "Abreu" "Arraez"
以下では、2年連続3割を超えた打者を探します。
そのための準備として、intersectの使い方を覚えます。
# intersectは2つのベクトルの中の共通の要素を取り出します。
intersect(1:50,30:80)
## [1] 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
# 2の倍数と3の倍数の共通要素(0〜50)は6の倍数
intersect(seq(0,50,by=2),seq(0,50,by=3))
## [1] 0 6 12 18 24 30 36 42 48
# 2年連続の3割打者はいない
intersect(bat2021,bat2022)
## character(0)
# 2年連続、2割8部超えは8人(ジャッジ、ボガーツ、ゴールドシュミット他)
tmp2021 = datb$last_name[datb$year==2021 & datb$batting_avg>0.28]
tmp2022 = datb$last_name[datb$year==2022 & datb$batting_avg>0.28]
intersect(tmp2021,tmp2022)
## [1] "Marte" "Judge" "Bogaerts" "Rosario" "Goldschmidt"
## [6] "Turner" "Freeman" "Bichette"
# 同様に、2年連続40本以上の選手は?
tmp2021 = datb$last_name[datb$year==2021 & datb$home_run>=40]
tmp2022 = datb$last_name[datb$year==2022 & datb$home_run>=40]
intersect(tmp2021,tmp2022) #いない
## character(0)
# 2年連続34本以上の選手は?
tmp2021 = datb$last_name[datb$year==2021 & datb$home_run>=34]
tmp2022 = datb$last_name[datb$year==2022 & datb$home_run>=34]
intersect(tmp2021,tmp2022) #(4人いた)
## [1] "Ohtani" "Judge" "Alonso" "Olson"
# 2年連続n本以上の選手を取り出す処理を関数化します。(BASICの復習)
getHomerunBatter = function(n){
tmp2021 = datb$last_name[datb$year==2021 & datb$home_run>=n]
tmp2022 = datb$last_name[datb$year==2022 & datb$home_run>=n]
intersect(tmp2021,tmp2022)
}
この関数を使うと、、
getHomerunBatter(40)
## character(0)
getHomerunBatter(39)
## [1] "Judge"
getHomerunBatter(38)
## [1] "Judge"
getHomerunBatter(37)
## [1] "Judge" "Alonso"
二刀流選手がいるかどうか調べましょう。。
# 打撃成績にも投手成績にもどちらにも顔を出している選手のIDは?
# (規定打席と規定投球回数のいずれも満たしている選手です)
intersect(datb$player_id,datp$player_id)
## [1] 660271
#誰だ?
#名前、成績年、打席数
datb[datb$player_id==660271,c("last_name","year","ab")]
## last_name year ab
## 27 Ohtani 2021 537
## 216 Ohtani 2022 586
#名前、成績年、投球回数
datp[datp$player_id==660271,c("last_name","year","p_formatted_ip")]
## last_name year p_formatted_ip
## 84 Ohtani 2021 130.1
## 179 Ohtani 2022 166.0