---
title: "統計データ解析I"
subtitle: "第3講 練習問題 解答例"
date: "`r Sys.time()`"
format:
    html: 
      toc: true
      html-math-method: katex
      self-contained: true
      grid: 
        margin-width: 350px
execute: 
  echo: true
  warning: false
reference-location: margin
citation-location: margin
tbl-cap-location: margin
fig-cap-location: margin
editor: visual
editor_options: 
  chunk_output_type: console
---

## 関数の使い方

### 問題

以下の問に答えなさい．

-   ヘルプ機能 (Help タブの検索窓, または関数 `help(),?`) を用いて関数 `sample()` を調べなさい．

-   サイコロを $1$ 回振る試行を模擬せよ．

-   サイコロを $10$ 回振る試行を模擬せよ．

    ::: callout-tip
    引数 `replace` を調べなさい．
    :::

-   $1$ が出易いサイコロを作りなさい．

    ::: callout-tip
    引数 `prob` を調べなさい．
    :::

-   $1$ から $6$ をランダムに並べ替えよ．

### 解答例

関数 `sample()` を調べる．

```{r}
help(sample)
```

サイコロを1回振る試行を模擬する．

```{r}
sample(x = 1:6, size = 1) # x = 見本点(標本点)の集合，size = 試行回数
```

サイコロを10回振る試行を模擬する．

```{r}
#| error: true
sample(x = 1:6, size = 10)                 # 復元しないのでエラーになる
sample(x = 1:6, size = 10, replace = TRUE)
```

$1$ が他の目の $3$ 倍出易いサイコロを作る．

```{r}
sample(1:6, 10,               # 引数名(x, size)を省略
       replace = TRUE,        # 復元抽出を行う
       prob = c(3, rep(1,5))) # =c(3,1,1,1,1,1) 各見本点の確率(重みづけ) 
```

$1$ から $6$ をランダムに並べ替える．

```{r}
sample(1:6, 6) # 既定値では復元抽出しないので並べ替えになる
```

## 自作関数の定義

### 問題

以下の問に答えなさい．

-   $1$ から整数 $n$ までの和を求める関数を作成せよ．

    ::: callout-tip
    関数 `sum()` を調べなさい． 等差数列の和を利用してもよい．
    :::

-   整数 $n$ の階乗 $n!$ を求める関数を作成せよ

    ::: callout-tip
    関数 `prod()` を調べなさい．
    :::

### 解答例

和 (summation) を計算する関数を定義する．

```{r}
my_sum <- function(n){
    out <- sum(1:n) # 1からnまでの整数を生成して和を求める
    return(out)
}
my_sum(10)          # 1から10までの和を確認する
```

以下のように定義することもできる．

```{r}
my_sum2 <- function(n){
    out <- n*(n+1)/2 # 等差数列の和を利用した場合
    return(out)
}
my_sum2(10)
```

階乗 (factorial) を計算する関数を定義する．

```{r}
my_fact <- function(n){
    out <- prod(1:n) # 1からnまでの整数を生成して積を求める
    return(out)
}
my_fact(5)           # 5!で確認する
```

::: callout-tip
関数内でオブジェクトに代入せずに直接 return() に渡すこともできる．

```{r}
#| eval: false
my_sum <- function(n){return(sum(1:n))}
```

また return() を省略すると，関数内の最後の計算結果が返値となるので，

```{r}
#| eval: false
my_sum <- function(n){sum(1:n)}
```

としても同じ結果となる．このような短縮した表記は無名関数の定義などで活用される．
:::

## 制御構造

### 問題

以下の問に答えなさい．

-   整数 $n$ の Fibonacci 数を求める関数を作成せよ．

    -   Fibonacci 数は以下の漸化式で計算される． $$
        \begin{align}
          F_{0}&=0\\
          F_{1}&=1\\
          F_{n}&=F_{n-1}+F_{n-2}
        \end{align}
        $$

-   行列 $X$ が与えられたとき，各列の平均を計算する関数を作成せよ．

-   前問で $X$ がベクトルの場合にはその平均を計算するように修正せよ．

    ::: callout-tip
    関数 `is.vector()` が利用できる．
    :::

### 解答例

Fibonacci 数を返す関数を定義する．

```{r}
my_fibo <- function(n){
    f0 <- 0           # 第0項の設定
    f1 <- 1           # 第1項の設定
    if(n<0) {
        print("計算できません")
        return(NA)    # 欠損値を返す
    }
    if(n==0) {        # n=0の場合
        return(f0)
    }
    if(n==1) {        # n=1の場合
        return(f1)
    }
    for(i in 2:n) {   # n>=2の場合
        fn <- f1 + f0 # fn = fn-1 + fn-2 の計算
        f0 <- f1      # fn-2 の値の更新 (f0が覚えておく)
        f1 <- fn      # fn-1 の値の更新 (f1が覚えておく)
    }
    return(fn)        # 計算結果を返す
}
```

Fibonacci 数を $n$ 項目まで計算する． $n$ が小さい場合には以下のように愚直に行えばよい．

```{r}
c(my_fibo(1),my_fibo(2),my_fibo(3),my_fibo(4),my_fibo(5),my_fibo(6))
```

同じ関数に複数の値を代入する方法はいくつか用意されているので， $n$ が大きい場合には例えば以下のように行えばよい．

```{r}
sapply(1:30, my_fibo) # sapplyを調べてみよ
```

行列の列ごとの平均を計算する関数を定義する．

```{r}
my_col_ave <- function(X) {
    ave <- rep(0, length = ncol(X))  # 平均を記録するベクトルを用意
    for(i in 1:ncol(X)){             # 列ごとに計算
        ave[i] <- sum(X[,i])/nrow(X) # 平均の定義に従って計算
        ## ave[i] <- mean(X[,i])     # 平均を計算する関数を用いても良い
    }
    return(ave)
  }
(A <- matrix(1:12,3,4,byrow=TRUE))   # 適当な行列を定義する
my_col_ave(A)                        # 正しい答えを返す
```

想定しない型を入力してみる．

```{r}
#| error: true
(x <- 1:12)   # 適当なベクトルを定義する
my_col_ave(x) # うまく動かない
```

ベクトルと行列を扱えるように修正する．

```{r}
my_col_ave <- function(X){ 
    if(is.vector(X)){  # ベクトルとそれ以外の場合分け
        ave <- mean(X) # ベクトルならば平均を計算
    } else {           # ベクトル以外は行列と想定して計算
        ave <- rep(0,length=ncol(X))
        for(i in 1:ncol(X)){
            ave[i] <- mean(X[,i])
        }
    }
    return(ave)
}
my_col_ave(A)
my_col_ave(x)          # いずれも正しい答えを返す
```
