Rのデータ操作を10分で入門する

## この記事は
Rを触ったことがない人向けによくあるデータ操作をRのライブラリを使い、
どのように実行するのかをまとめました。
この記事を読めばRの基本的なデータ操作をマスターできます。
RにはPythonのPandas的なライブラリが３つあります。
まずはこの３つの使い分けから見ていきましょう。

## ライブラリの選び方

| 用途 | 推奨ライブラリ |
| --- | --- |
| **軽量で標準的な操作** | `data.frame`（R標準） |
| **大規模データ/高速処理** | `data.table` |
| **可読性・チェーン操作重視** | `tibble` + `dplyr`（tidyverse） |

## 基本セットアップとサンプルデータ

まず必要なライブラリを読み込み、サンプルデータを作成します：

```r
# ライブラリ読み込み
library(tidyverse)
library(data.table)

# サンプルデータ作成
set.seed(123)
df <- tibble(
  id       = 1:12,
  category = rep(c("A","B","C"), each = 4),
  value    = c(5, 12, 7, 20,  3, 15, 8, 25, 10, 30, 2, 18),
  count    = sample(1:5, 12, replace = TRUE)
)
print(df)

# data.table用にデータフレームを変換
dt <- as.data.table(df)
print(dt)

```

## data.frame による操作

### dfの基本

`data.frame` では `df[行指定, 列指定]` 構文を使用して行と列を指定します：

```r
# 基本構文: df[行指定, 列指定]
# 例: 最初の3行と、id・valueの列だけを選択
subset_example <- df[1:3, c("id", "value")]
print("=== 行と列の選択例 ===")
print(subset_example)

# 全ての行を選択する場合は空欄
all_rows_example <- df[, c("id", "category")]
print("=== 全ての行から特定の列を選択 ===")
print(all_rows_example)

# 全ての列を選択する場合も空欄
all_cols_example <- df[1:2, ]
print("=== 特定の行から全ての列を選択 ===")
print(all_cols_example)

```

### 1. 列の選択

特定の列だけを選択するには、列名のベクトルを指定します：

```r
# id, category, value_per_count のみを選択
df_with_ratio <- transform(df, value_per_count = value / count)
df_selected <- df_with_ratio[, c("id", "category", "value_per_count")]

print(df_selected)

```

### 2. 列の追加

`transform()`を使って新しい列を追加できます：

```r
# value を count で割った新しい列を追加
df_with_ratio <- transform(df,
  value_per_count = value / count
)

print(df_with_ratio)

```

### 3. データフレームの絞り込み

`data.frame`では、条件に基づいて行を絞り込む際に `df[条件, ]` 構文を使用します：

```r
# value が 10より大きい行を抽出
df_filtered <- df[df$value > 10, ]
print("=== df_filtered (value > 10) ===")
print(df_filtered)

```

### 4. グループ集計

`aggregate()`関数を使用して、グループごとの集計を行います：

```r
# カテゴリごとに value の合計を計算
# フォーミュラ構文: 左辺(集計対象) ~ 右辺(グループ化キー)
total_value <- aggregate(value ~ category, data = df, FUN = sum)

# カテゴリごとに count の平均を計算
avg_count <- aggregate(count ~ category, data = df, FUN = mean)

# 結果を結合し、列名を変更
df_grouped <- merge(total_value, avg_count, by = "category")
names(df_grouped) <- c("category", "total_value", "avg_count")

print(df_grouped)

```

## data.table による操作

### data.tableの基本

`data.table`は大規模データセットを効率的に処理するためのパッケージで、独自の構文 `DT[i, j, by]` を使用します：

- `i`: どの行を選択するか（WHERE）
- `j`: 何を計算/選択するか（SELECT）
- `by`: どのグループで計算するか（GROUP BY）

```r
# data.tableの基本構文: DT[i, j, by]
print("=== data.tableの基本例 ===")

# 例: value > 10の行から、idとcategoryのみを選択
basic_example <- dt[value > 10, .(id, category)]
print(basic_example)

# 例: categoryごとにvalueの合計を計算
group_example <- dt[, .(total_value = sum(value)), by = category]
print(group_example)

```

### 1. 列の選択

data.tableでは列を選択するために `.(列名1, 列名2, ...)` または `list(列名1, 列名2, ...)` 構文を使用します：

```r
# id, category, value_per_count のみを選択
dt[, value_per_count := value / count]  # 一時的に列を追加
dt_selected <- dt[, .(id, category, value_per_count)]

print(dt_selected)

# 別の方法: j に列名のベクトルを使用
dt_selected2 <- dt[, c("id", "category", "value_per_count"), with = FALSE]
print(dt_selected2)

```

### 2. 列の追加

data.tableでは `:=` 演算子を使って列を追加・変更します（参照セマンティクス）：

```r
# value を count で割った新しい列を追加
dt[, value_per_count := value / count]
print(dt)

# 複数の列を一度に追加
dt[, `:=`(
  squared_value = value^2,
  doubled_count = count*2
)]
print(dt)

```

### 3. データフレームの絞り込み

data.tableでは`i`の位置に条件を指定して行を絞り込みます：

```r
# value が 10より大きい行を抽出
dt_filtered <- dt[value > 10]
print("=== dt_filtered (value > 10) ===")
print(dt_filtered)

# 複数条件での絞り込み
dt_filtered2 <- dt[value > 10 & category == "A"]
print("=== dt_filtered2 (value > 10 & category == 'A') ===")
print(dt_filtered2)

```

### 4. グループ集計

data.tableでは `by` 引数を使用してグループ集計を行います：

```r
# カテゴリごとに value の合計と count の平均を計算
dt_grouped <- dt[, .(
  total_value = sum(value),
  avg_count = mean(count)
), by = category]

print(dt_grouped)

# 複数の列でグループ化する場合
dt[, .(avg_value = mean(value)), by = .(category, count > 2)]

```

## tidyverse による操作

### パイプ演算子 `%>%` の基本

パイプ演算子は左側のオブジェクトを右側の関数の第1引数として渡します：

```r
df %>% filter(value > 10)  # df を filter() の第1引数に渡す

```

例えば以下の処理：

```
df
id category value count
1  A        12    5
2  A        20    1
3  B        9     1
4  C        2     2
5  D        5     1

```

に対して：

```r
df %>%
  filter(value > 10) %>%
  mutate(ratio = value / count) %>%
  select(id, ratio)

```

結果：

```
id ratio
1  2.4   # 12/5 = 2.4
2  20.0  # 20/1 = 20.0

```

### 1. 列の選択

`select()`関数で特定の列を選びます：

```r
# id, category, value_per_count のみを選択
df_with_ratio <- df %>% mutate(value_per_count = value / count)
df_selected <- df_with_ratio %>%
  select(id, category, value_per_count)

print(df_selected)

```

### 2. 列の追加

`mutate()`関数で新しい列を追加できます：

```r
# value を count で割った新しい列を追加
df_with_ratio <- df %>%
  mutate(
    value_per_count = value / count
  )

print(df_with_ratio)

```

### 3. データフレームの絞り込み

tidyverseでは`filter()`関数を使います：

```r
# value が 10より大きい行を抽出
df_filtered <- df %>%
  filter(value > 10)

print(df_filtered)

```

### 4. グループ集計

`group_by()`と`summarise()`を組み合わせます：

```r
# カテゴリごとに value の合計と count の平均を計算
df_grouped <- df %>%
  group_by(category) %>%
  summarise(
    total_value = sum(value),
    avg_count = mean(count),
    .groups = "drop"  # 集計後にグループ化を解除
  )

print(df_grouped)

```

## ライブラリ別性能比較

| 特徴 | data.frame | data.table | tidyverse |
| --- | --- | --- | --- |
| **速度** | 標準的 | 非常に高速 | 標準より少し速い |
| **メモリ効率** | 標準的 | 非常に効率的 | 標準より少し効率的 |
| **構文の読みやすさ** | やや複雑 | 独特だが簡潔 | 非常に読みやすい |
| **大規模データ向け** | × | ◎ | ○ |
| **学習曲線** | 標準的 | やや急 | 緩やか |

## 最後に

- **data.frame**: R標準のデータ操作で、`[]`による添字操作や`aggregate()`などの関数を使用
- **data.table**: 大規模データ向けに最適化された高速処理が可能、`DT[i, j, by]`構文で簡潔かつパワフルな操作
- **tidyverse**: パイプ演算子`%>%`を活用した直感的で読みやすいコード、`select()`, `mutate()`, `filter()`, `group_by()`などの関数で流れるようにデータ操作

それぞれのライブラリは特性に応じて使い分けることで、効率的なデータ分析が可能です。初心者は読みやすいtidyverseから始め、大規模データ処理が必要になったらdata.tableを検討するとよいでしょう。