【R言語】rowSums/colMeans徹底解説!データ分析の基本をマスター
これはデータ分析において非常に頻繁に行われる操作であり、Rにはこれを効率的に実行するための専用の関数が用意されています。
主な関数
以下の4つの関数が基本となります。
colMeans(x, na.rm = FALSE, dims = 1)
:x
の列ごとの平均を計算します。rowMeans(x, na.rm = FALSE, dims = 1)
:x
の行ごとの平均を計算します。colSums(x, na.rm = FALSE, dims = 1)
:x
の列ごとの合計を計算します。rowSums(x, na.rm = FALSE, dims = 1)
:x
の行ごとの合計を計算します。
引数の説明
dims
: どの次元にわたって合計/平均を計算するかを指定します。通常、行列や2次元のデータフレームではデフォルトの1
で問題ありません。多次元配列の場合に役立ちます。na.rm
: 欠損値(NA
)を計算から除外するかどうかを指定する論理値(TRUE
またはFALSE
)。FALSE
(デフォルト): 欠損値が含まれる行または列の合計/平均はNA
になります。TRUE
: 欠損値を無視して計算を行います。
x
: 合計または平均を計算する対象となる数値の配列(行列やデータフレームなど)。
具体例
簡単なデータフレームを使って説明します。
# サンプルデータフレームを作成
data <- data.frame(
科目A = c(80, 90, NA, 75),
科目B = c(60, 85, 70, 95),
科目C = c(70, 75, 80, 65)
)
print(data)
出力:
科目A 科目B 科目C
1 80 60 70
2 90 85 75
3 NA 70 80
4 75 95 65
行ごとの合計
生徒ごとの合計点(欠損値を除く)を計算してみましょう。
# 行ごとの合計を計算(欠損値NAを無視)
row_sums <- rowSums(data, na.rm = TRUE)
print(row_sums)
[1] 210 250 150 235
ここで、3行目の生徒は科目AがNA
ですが、na.rm = TRUE
のおかげで、科目Bと科目Cの合計70 + 80 = 150
が計算されています。もしna.rm = FALSE
(デフォルト) であれば、3行目の合計はNA
になります。
# 行ごとの合計を計算(欠損値NAを含む場合はNA)
row_sums_na <- rowSums(data, na.rm = FALSE)
print(row_sums_na)
[1] 210 250 NA 235
列ごとの平均
科目ごとの平均点を計算してみましょう。
# 列ごとの平均を計算(欠損値NAを無視)
col_means <- colMeans(data, na.rm = TRUE)
print(col_means)
科目A 科目B 科目C
81.66667 77.50000 72.50000
科目Aの平均は(80 + 90 + 75) / 3 = 81.66667
となり、NA
の行は除外されています。
これらのrowSums
, colSums
, rowMeans
, colMeans
関数は、より汎用的なapply()
関数と似たような処理を行いますが、これらの専用関数はより高速に動作するという利点があります。
apply()
関数を使って同じことをしようとすると、以下のようになります。
# apply() を使って行ごとの合計を計算
# apply(X, MARGIN, FUN)
# MARGIN=1 は行、MARGIN=2 は列
row_sums_apply <- apply(data, 1, sum, na.rm = TRUE)
print(row_sums_apply)
# apply() を使って列ごとの平均を計算
col_means_apply <- apply(data, 2, mean, na.rm = TRUE)
print(col_means_apply)
結果は同じですが、大規模なデータセットを扱う場合は、rowSums
やcolMeans
などの専用関数を使用する方がパフォーマンスが良いです。
よくあるエラーとその原因
-
- 原因: このエラーは、計算対象のデータフレームや行列に数値以外のデータ(例: 文字列、因子型)が含まれている場合に発生します。これらの関数は、数値データのみを処理するように設計されています。
- トラブルシューティング:
- データの型を確認する:
str()
関数やclass()
関数を使って、問題のデータフレームや列のデータ型を確認します。data_with_char <- data.frame( A = c(1, 2, 3), B = c("x", "y", "z") # 文字列が含まれている ) # colSums(data_with_char) # エラーが発生する str(data_with_char)
- 非数値の列を除外する:
rowSums()
やcolMeans()
を適用する前に、数値データを含む列のみを選択します。numeric_data <- data_with_char[, sapply(data_with_char, is.numeric)] colSums(numeric_data)
- 非数値の列を数値に変換する: もし文字列が数値として解釈できる場合は、
as.numeric()
を使って変換を試みます。ただし、変換できない文字列があるとNA
になることに注意してください。data_as_numeric <- data.frame( A = as.numeric(data_with_char$A), B = as.numeric(as.character(data_with_char$B)) # 因子型の場合は as.character() を挟む ) # colSums(data_as_numeric, na.rm = TRUE) # 必要に応じて na.rm を指定
- データの型を確認する:
-
Error in rowSums(data) : 'x' must be an array of at least two dimensions
- 原因:
rowSums()
やcolSums()
は、行列 (matrix) やデータフレーム (data.frame) のように、少なくとも2つの次元を持つデータ構造に対して設計されています。単一のベクトルに対してこれらの関数を直接適用しようとすると、このエラーが発生します。 - トラブルシューティング:
- ベクトルを行列に変換する:
as.matrix()
を使用してベクトルを行列に変換するか、matrix()
関数で直接行列を作成します。my_vector <- c(1, 2, 3, 4, 5, 6) # rowSums(my_vector) # エラーが発生する my_matrix <- as.matrix(my_vector) rowSums(my_matrix) # 期待通りに動作する
- 適切な関数を使用する: 単純にベクトルの合計や平均を計算したい場合は、
sum()
やmean()
関数を使用します。sum(my_vector) mean(my_vector)
- ベクトルを行列に変換する:
- 原因:
-
Error: object '...' not found
- 原因: これはRで非常によくある一般的なエラーで、指定したオブジェクト(変数、データフレーム、列名など)が見つからない場合に発生します。スペルミス、オブジェクトがまだ作成されていない、または現在の環境に存在しないなどが原因です。
- トラブルシューティング:
- スペルミスを確認する: 関数名、変数名、列名にスペルミスがないか注意深く確認します。大文字と小文字も区別されます。
- オブジェクトが存在することを確認する:
ls()
やexists()
関数を使って、オブジェクトが現在の環境に存在するか確認します。 - データフレームの列に正しくアクセスしているか確認する:
data$column_name
やdata[["column_name"]]
、data[, "column_name"]
のように、正しい構文で列にアクセスしているか確認します。my_df <- data.frame(A = 1:3, B = 4:6) # colSums(my_df$C) # エラー: object 'C' not found (C列が存在しないため) colSums(my_df$A) # 正しい
-
NA
が返される(期待と異なる結果)- 原因: データに欠損値 (
NA
) が含まれており、na.rm = FALSE
(デフォルト) の設定になっている場合、欠損値を含む行や列の合計/平均がNA
になります。 - トラブルシューティング:
na.rm = TRUE
を指定する: 欠損値を計算から除外したい場合は、na.rm = TRUE
を引数に指定します。data_with_na <- data.frame( X = c(1, 2, NA), Y = c(4, NA, 6) ) rowSums(data_with_na) # NA を含む行が NA になる rowSums(data_with_na, na.rm = TRUE) # NA を無視して計算
- 欠損値の有無を確認する:
is.na()
やsum(is.na(data))
などでデータ内の欠損値の数を確認します。 - 欠損値の処理方法を検討する: 欠損値を無視する(
na.rm = TRUE
)以外にも、欠損値を補完する(例:na.omit()
,na.exclude()
,tidyr::drop_na()
,impute()
など)といった方法もあります。
- 原因: データに欠損値 (
-
dplyr
やtidyverse
との組み合わせで問題が発生する- 原因:
dplyr
のrowwise()
などと組み合わせた場合、rowSums()
/rowMeans()
の挙動が期待と異なることがあります。これは、rowwise()
がグループ化されたデータフレームを作成するため、関数が各行の単一のデータフレームに対して適用されるためです。 - トラブルシューティング:
c_across()
またはacross()
を使用する:dplyr
の新しいバージョンでは、c_across()
やacross()
を使って複数の列を対象に処理を行うのが推奨されます。library(dplyr) my_data <- tibble( id = 1:3, val1 = c(10, 20, 30), val2 = c(15, 25, NA), val3 = c(5, 10, 15) ) # rowwise() と c_across() を使った例 my_data %>% rowwise() %>% mutate(row_sum = sum(c_across(val1:val3), na.rm = TRUE)) %>% ungroup() # across() を使った例 (rowwise() なしで列全体に適用) # これはrowSums/colSumsの代わりにはならないが、列ごとの集計に使える my_data %>% summarise( across(val1:val3, mean, na.rm = TRUE) )
apply()
を検討する:dplyr
のパイプライン内でrowSums()
やrowMeans()
を直接使いたい場合は、apply()
と組み合わせることも可能です。my_data %>% mutate(row_sum = apply(select(., val1:val3), 1, sum, na.rm = TRUE))
ungroup()
する: もしデータが意図せずグループ化されている場合は、ungroup()
を実行することで問題が解決することがあります。
- 原因:
- 検索エンジンを利用する: エラーメッセージをそのままコピーして検索エンジンに入力すると、同じ問題に遭遇した他の開発者の解決策が見つかることが多いです(Stack Overflowなど)。
- 小さなサンプルデータで試す: 問題を再現できる最小限のサンプルデータを作成し、そのデータでエラーの原因を特定しようとします。
- ヘルプドキュメントを参照する: 疑問に思った関数は
?function_name
でヘルプドキュメントを確認しましょう。引数の意味や使用例が記載されています。 - 段階的に実行する: 複雑なコードの場合は、一度に全てを実行するのではなく、コードを小さな部分に分割して、それぞれの部分が期待通りに動作するか確認します。
- データの構造を確認する:
str()
,class()
,dim()
,head()
などの関数を使って、入力データの構造、型、次元、内容を確認します。 - エラーメッセージをよく読む: Rのエラーメッセージは、どこに問題があるかを示す重要な手がかりです。メッセージを注意深く読み、示されている行番号やオブジェクト名に注目しましょう。
基本的な使い方 (データフレームと行列)
最も一般的なのは、データフレームや行列に対して使用するケースです。
# ------------------------------------
# 1. 基本的な使い方
# ------------------------------------
# データフレームの作成
df <- data.frame(
SubjectA = c(85, 92, 78, 65, 90),
SubjectB = c(70, 88, 95, 72, 80),
SubjectC = c(90, 75, 82, 88, 93)
)
print("--- 元のデータフレーム ---")
print(df)
# 行ごとの合計 (rowSums)
# 生徒ごとの合計点
row_sums_df <- rowSums(df)
print("--- データフレームの行ごとの合計 ---")
print(row_sums_df)
# 行ごとの平均 (rowMeans)
# 生徒ごとの平均点
row_means_df <- rowMeans(df)
print("--- データフレームの行ごとの平均 ---")
print(row_means_df)
# 列ごとの合計 (colSums)
# 科目ごとの合計点
col_sums_df <- colSums(df)
print("--- データフレームの列ごとの合計 ---")
print(col_sums_df)
# 列ごとの平均 (colMeans)
# 科目ごとの平均点
col_means_df <- colMeans(df)
print("--- データフレームの列ごとの平均 ---")
print(col_means_df)
# 行列 (matrix) の作成
mat <- matrix(
c(10, 20, 30, 40, 50, 60),
nrow = 3, byrow = TRUE,
dimnames = list(
c("R1", "R2", "R3"),
c("C1", "C2")
)
)
print("--- 元の行列 ---")
print(mat)
# 行列に対しても同様に適用可能
row_sums_mat <- rowSums(mat)
print("--- 行列の行ごとの合計 ---")
print(row_sums_mat)
col_means_mat <- colMeans(mat)
print("--- 行列の列ごとの平均 ---")
print(col_means_mat)
欠損値 (NA) の扱い
データに欠損値が含まれる場合、na.rm = TRUE
を指定して欠損値を除外して計算することができます。
# ------------------------------------
# 2. 欠損値 (NA) の扱い
# ------------------------------------
# 欠損値を含むデータフレームの作成
df_na <- data.frame(
Var1 = c(10, 20, NA, 40),
Var2 = c(5, NA, 15, 25),
Var3 = c(NA, 30, 35, 45)
)
print("--- NAを含むデータフレーム ---")
print(df_na)
# na.rm = FALSE (デフォルト): NAが含まれると結果もNA
print("--- na.rm = FALSE (デフォルト) の結果 ---")
print(rowSums(df_na))
print(colMeans(df_na))
# na.rm = TRUE: NAを除外して計算
print("--- na.rm = TRUE の結果 ---")
row_sums_na_rm <- rowSums(df_na, na.rm = TRUE)
print("行ごとの合計 (NA除去):")
print(row_sums_na_rm)
col_means_na_rm <- colMeans(df_na, na.rm = TRUE)
print("列ごとの平均 (NA除去):")
print(col_means_na_rm)
# NAの有無を確認する
print("--- NAの有無確認 ---")
print(is.na(df_na))
print(colSums(is.na(df_na))) # 各列のNAの数
計算結果を既存のデータフレームに追加する
計算した合計や平均を、元のデータフレームに新しい列として追加することはよく行われます。
# ------------------------------------
# 3. 計算結果を既存のデータフレームに追加
# ------------------------------------
df_add <- data.frame(
Math = c(80, 90, 75),
Science = c(70, 85, 90),
English = c(95, 70, 80)
)
print("--- 元のデータフレーム ---")
print(df_add)
# 行ごとの合計と平均を新しい列として追加
df_add$TotalScore <- rowSums(df_add)
df_add$AverageScore <- rowMeans(df_add)
print("--- 合計と平均を追加したデータフレーム ---")
print(df_add)
# 列ごとの合計と平均を、データフレームの下に新しい行として追加(ややトリッキーだが可能)
# 列名を揃えるために、それぞれの平均を1行のデータフレームにする
col_means_df_add <- as.data.frame(t(colMeans(df_add[, c("Math", "Science", "English")])))
colnames(col_means_df_add) <- c("Math", "Science", "English") # 列名が一致するようにする
df_final <- rbind(df_add, "Overall_Mean" = col_means_df_add)
print("--- 列の平均を最終行に追加したデータフレーム ---")
print(df_final)
# または、列の合計/平均は別途ベクトルとして管理する方が一般的
col_sums_final <- colSums(df_add[, c("Math", "Science", "English")])
print("--- 列ごとの合計(別途ベクトル)---")
print(col_sums_final)
特定の列にのみ適用する
データフレームの一部の列にのみ合計や平均を適用したい場合があります。
# ------------------------------------
# 4. 特定の列にのみ適用
# ------------------------------------
sales_data <- data.frame(
Region = c("East", "West", "North", "South"),
Q1 = c(100, 150, 120, 180),
Q2 = c(110, 160, 130, 190),
Q3 = c(120, 170, 140, 200),
OtherInfo = c("A", "B", "C", "D")
)
print("--- 売上データ ---")
print(sales_data)
# 'Region' や 'OtherInfo' を除いて、Q1, Q2, Q3 の列にのみ行合計を適用
# 方法1: 列名を指定してサブセットを作成
sales_data$TotalQuarterSales_by_name <- rowSums(sales_data[, c("Q1", "Q2", "Q3")])
print("--- 列名指定で合計を追加 ---")
print(sales_data)
# 方法2: 列のインデックスを指定
sales_data$TotalQuarterSales_by_index <- rowSums(sales_data[, 2:4])
print("--- 列インデックス指定で合計を追加 ---")
print(sales_data)
# 方法3: 非数値の列を除外(汎用的)
numeric_cols_only <- sales_data[, sapply(sales_data, is.numeric)]
sales_data$TotalSales_NumericOnly <- rowSums(numeric_cols_only)
print("--- 数値列のみで合計を追加 ---")
print(sales_data)
tidyverse
パッケージ群の dplyr
を使うと、より柔軟で読みやすいコードで同じような操作を行うことができます。
# ------------------------------------
# 5. dplyr を使った応用例
# ------------------------------------
# dplyr パッケージをロード(インストールされていない場合は install.packages("dplyr"))
library(dplyr)
df_dplyr <- data.frame(
id = 1:5,
score_math = c(80, 90, 75, 88, 92),
score_science = c(70, 85, 92, 78, 80),
score_english = c(95, 70, 80, 85, 90),
gender = c("M", "F", "M", "F", "M")
)
print("--- dplyrでの操作対象データ ---")
print(df_dplyr)
# 行ごとの合計と平均を計算し、新しい列を追加 (acrossとrowwiseを使用)
# score_ で始まるすべての列に適用
df_dplyr_processed <- df_dplyr %>%
rowwise() %>% # 行ごとの処理を行うことを指定
mutate(
TotalScore = sum(c_across(starts_with("score_")), na.rm = TRUE),
AverageScore = mean(c_across(starts_with("score_")), na.rm = TRUE)
) %>%
ungroup() # rowwise() を解除して、通常のデータフレームに戻す
print("--- dplyrで計算結果を追加したデータ ---")
print(df_dplyr_processed)
# 列ごとの合計と平均を計算 (summarise と across を使用)
col_summary_dplyr <- df_dplyr %>%
summarise(
across(starts_with("score_"), sum, .names = "Sum_{.col}", na.rm = TRUE),
across(starts_with("score_"), mean, .names = "Mean_{.col}", na.rm = TRUE)
)
print("--- dplyrで列ごとの合計と平均をまとめたデータ ---")
print(col_summary_dplyr)
apply() 関数を使用する
apply()
関数は、配列(行列やデータフレームなど)のマージン(行または列)にわたって関数を適用するための汎用的な関数です。rowSums()
などがない時代から使われており、任意の関数を適用できる点が強みです。
- 構文
apply(X, MARGIN, FUN, ...)
X
: 処理する配列(データフレームや行列)MARGIN
: 1は行ごと、2は列ごとFUN
: 適用する関数(例:sum
,mean
,sd
,min
,max
など)...
:FUN
に渡す追加の引数(例:na.rm = TRUE
)
# サンプルデータ
data_mat <- matrix(c(1, 2, NA, 4, 5, 6, 7, 8, 9), nrow = 3, byrow = TRUE)
colnames(data_mat) <- c("Col1", "Col2", "Col3")
rownames(data_mat) <- c("Row1", "Row2", "Row3")
print("--- 元の行列 ---")
print(data_mat)
# 行ごとの合計
row_sums_apply <- apply(data_mat, 1, sum, na.rm = TRUE)
print("--- apply() を使った行ごとの合計 ---")
print(row_sums_apply)
# 列ごとの平均
col_means_apply <- apply(data_mat, 2, mean, na.rm = TRUE)
print("--- apply() を使った列ごとの平均 ---")
print(col_means_apply)
# apply() の利点: 任意の関数を適用できる
row_stdevs_apply <- apply(data_mat, 1, sd, na.rm = TRUE)
print("--- apply() を使った行ごとの標準偏差 ---")
print(row_stdevs_apply)
メリット
- 多次元配列(3次元以上)にも対応可能。
sum
やmean
だけでなく、任意の関数(標準偏差sd
、中央値median
、最小値min
など、ユーザー定義関数も含む)を適用できる汎用性。
デメリット
rowSums()
などに比べて、計算速度が遅い場合がある(特に大規模データセットの場合)。これは、apply()
が内部でループ処理を行うためです。
dplyr パッケージの rowwise() と across() / c_across() を使用する
tidyverse
の一部である dplyr
パッケージは、データ操作に非常に強力で人気があります。行ごとの計算には rowwise()
と across()
(または c_across()
) を組み合わせるのが現代的なRのイディオムです。列ごとの集計には、summarise()
と across()
を使います。
# dplyr パッケージをロード(インストールされていない場合は install.packages("dplyr"))
library(dplyr)
# サンプルデータフレーム
df_dplyr <- data.frame(
ID = 1:4,
ScoreA = c(10, 20, NA, 40),
ScoreB = c(15, NA, 25, 35),
ScoreC = c(20, 30, 35, NA)
)
print("--- 元のデータフレーム (dplyr) ---")
print(df_dplyr)
# 行ごとの合計と平均を計算し、新しい列として追加
# starts_with("Score") でScoreで始まるすべての列を選択
df_dplyr_processed <- df_dplyr %>%
rowwise() %>% # これ以降の操作を「行ごと」に行うように指定
mutate(
TotalScore = sum(c_across(starts_with("Score")), na.rm = TRUE),
AverageScore = mean(c_across(starts_with("Score")), na.rm = TRUE)
) %>%
ungroup() # rowwise() モードを解除しないと、後続の操作に影響が出ることがある
print("--- dplyr で行ごとの合計と平均を追加 ---")
print(df_dplyr_processed)
# 列ごとの合計と平均を計算
col_summary_dplyr <- df_dplyr %>%
summarise(
# Scoreで始まる各列に対して sum 関数を適用し、列名を "Sum_" + 元の列名 にする
across(starts_with("Score"), sum, na.rm = TRUE, .names = "Sum_{.col}"),
# Scoreで始まる各列に対して mean 関数を適用し、列名を "Mean_" + 元の列名 にする
across(starts_with("Score"), mean, na.rm = TRUE, .names = "Mean_{.col}")
)
print("--- dplyr で列ごとの合計と平均をまとめる ---")
print(col_summary_dplyr)
メリット
- 欠損値の扱い
na.rm = TRUE
を簡単に指定できる。 - データフレーム全体で一貫した操作
dplyr
のエコシステム内でデータ操作を一貫して行える。 - 柔軟な列選択
starts_with()
,ends_with()
,contains()
,num_range()
など、across()
内のヘルパー関数を使って、パターンに基づいて複数の列を簡単に選択できる。 - コードの可読性
パイプ%>%
と組み合わせることで、データ操作のステップが非常に明確になる。
デメリット
dplyr
パッケージの知識が必要。rowSums()
などに比べて、わずかにオーバーヘッドがある場合がある(非常に大規模なデータでパフォーマンスが求められる場合)。
data.table
は、Rのデータフレームを非常に高速に処理するためのパッケージです。特に大規模なデータセットに対して高速な集計操作を行いたい場合に強力な選択肢となります。
# data.table パッケージをロード(インストールされていない場合は install.packages("data.table"))
library(data.table)
# サンプルデータフレームを data.table に変換
dt <- data.table(
ID = 1:4,
ScoreA = c(10, 20, NA, 40),
ScoreB = c(15, NA, 25, 35),
ScoreC = c(20, 30, 35, NA)
)
print("--- 元の data.table ---")
print(dt)
# 行ごとの合計と平均を計算し、新しい列として追加
# .SD は "Subset of Data" を意味し、指定した列のサブセットを指す
# .SDcols で対象列を指定
dt[, TotalScore := rowSums(.SD, na.rm = TRUE), .SDcols = c("ScoreA", "ScoreB", "ScoreC")]
dt[, AverageScore := rowMeans(.SD, na.rm = TRUE), .SDcols = c("ScoreA", "ScoreB", "ScoreC")]
print("--- data.table で行ごとの合計と平均を追加 ---")
print(dt)
# 列ごとの合計と平均を計算
# lapply(.SD, FUN, ...) は .SD の各列に FUN を適用する
col_summary_dt <- dt[, lapply(.SD, function(x) list(Sum = sum(x, na.rm = TRUE), Mean = mean(x, na.rm = TRUE))),
.SDcols = c("ScoreA", "ScoreB", "ScoreC")]
print("--- data.table で列ごとの合計と平均をまとめる ---")
print(col_summary_dt)
# よりシンプルな列ごとの平均の取得 (直接列名を指定)
col_means_dt_simple <- dt[, .(Mean_ScoreA = mean(ScoreA, na.rm = TRUE),
Mean_ScoreB = mean(ScoreB, na.rm = TRUE),
Mean_ScoreC = mean(ScoreC, na.rm = TRUE))]
print("--- data.table でシンプルに列ごとの平均を取得 ---")
print(col_means_dt_simple)
メリット
- 簡潔な構文
慣れると非常に簡潔に記述できる。 - メモリ効率
メモリを効率的に使用する設計。 - 非常に高速
大規模データセットに対して、rowSums()
などよりもさらに高速なパフォーマンスを発揮することが多い。
- パッケージへの依存。
- 学習曲線
data.table
独自の構文(DT[i, j, by]
)に慣れるまで時間がかかる場合がある。
- 大規模データ、パフォーマンス重視
data.table
- 大量のデータを扱う場合に、処理速度とメモリ効率で優れる。
- 任意の関数を適用したい場合
apply()
- 柔軟性が高く、標準偏差やカスタム関数など、さまざまな集計関数を適用できる。
- 最も速く、シンプル
rowSums()
,colSums()
,rowMeans()
,colMeans()
- 数値データのみで、行/列の合計/平均を計算する最も効率的な方法。