web-dev-qa-db-ja.com

複数のメジャー列を持つロングフォーマットからワイドフォーマットにデータを変換する

持ちたいメジャー変数が複数ある場合、データをロングフォーマットからワイドフォーマットに切り替える最もエレガントで柔軟な方法を理解するのに苦労しています。

たとえば、次は長い形式の単純なデータフレームです。 IDは主語、TIMEは時間変数、XYIDTIME

_> my.df <- data.frame(ID=rep(c("A","B","C"), 5), TIME=rep(1:5, each=3), X=1:15, Y=16:30)
> my.df

   ID TIME  X  Y
1   A    1  1 16
2   B    1  2 17
3   C    1  3 18
4   A    2  4 19
5   B    2  5 20
6   C    2  6 21
7   A    3  7 22
8   B    3  8 23
9   C    3  9 24
10  A    4 10 25
11  B    4 11 26
12  C    4 12 27
13  A    5 13 28
14  B    5 14 29
15  C    5 15 30
_

TIMEの値をインクルードXを含む列ヘッダーに変換したいだけの場合は、reshapeパッケージからcast()を使用できることを知っています(またはdcast() from _reshape2_):

_> cast(my.df, ID ~ TIME, value="X")
  ID 1 2 3  4  5
1  A 1 4 7 10 13
2  B 2 5 8 11 14
3  C 3 6 9 12 15
_

しかし、私が本当にやりたいことは、Yを別のメジャー変数として持ち込み、列名にメジャー変数名と時間値の両方を反映させることです。

_  ID X_1 X_2 X_3  X_4 X_5 Y_1 Y_2 Y_3 Y_4 Y_5
1  A   1   4   7   10  13  16  19  22  25  28
2  B   2   5   8   11  14  17  20  23  26  29
3  C   3   6   9   12  15  18  21  24  27  30
_

(FWIW、すべてのXの後にYが続いているかどうか、または_X_1_、_Y_1_、_X_2_、_Y_2_など)

cast-長いデータを2回実行して結果をマージすることでこれに近づくことができますが、列名にはいくつかの作業が必要であり、3番目または4番目を追加する必要がある場合は調整する必要がありますXおよびYに加えて変数:

_merge(
cast(my.df, ID ~ TIME, value="X"),
cast(my.df, ID ~ TIME, value="Y"),
by="ID", suffixes=c("_X","_Y")
)
_

_reshape2_およびplyrの関数のいくつかの組み合わせは、私の試みよりもエレガントにこれを実行でき、複数のメジャー変数をよりきれいに処理できるようです。 cast(my.df, ID ~ TIME, value=c("X","Y"))のようなものは無効です。しかし、私はそれを理解することができませんでした。

35
colonel.triq

必要に応じて複数の変数を処理するには、キャストする前に、データをmeltする必要があります。

library("reshape2")

dcast(melt(my.df, id.vars=c("ID", "TIME")), ID~variable+TIME)

与える

  ID X_1 X_2 X_3 X_4 X_5 Y_1 Y_2 Y_3 Y_4 Y_5
1  A   1   4   7  10  13  16  19  22  25  28
2  B   2   5   8  11  14  17  20  23  26  29
3  C   3   6   9  12  15  18  21  24  27  30

コメントに基づいて編集:

データフレーム

num.id = 10 
num.time=10 
my.df <- data.frame(ID=rep(LETTERS[1:num.id], num.time), 
                    TIME=rep(1:num.time, each=num.id), 
                    X=1:(num.id*num.time), 
                    Y=(num.id*num.time)+1:(2*length(1:(num.id*num.time))))

ID/TIMEの組み合わせは一意の行を示さないため、異なる結果が得られます(すべてのエントリは2です)。実際、ID/TIMEの組み合わせごとに2つの行があります。 reshape2は、変数の可能な組み合わせごとに単一の値を想定し、複数のエントリがある場合に集計関数を適用して単一の変数を作成します。それが警告がある理由です

Aggregation function missing: defaulting to length

あなたはその冗長性を壊す別の変数を追加する場合に機能する何かを得ることができます。

my.df$cycle <- rep(1:2, each=num.id*num.time)
dcast(melt(my.df, id.vars=c("cycle", "ID", "TIME")), cycle+ID~variable+TIME)

cycle/ID/timemy.dfの行を一意に定義するようになったため、これは機能します。

16
Brian Diggs
   reshape(my.df,
           idvar = "ID",
           timevar = "TIME",
           direction = "wide")

与える

  ID X.1 Y.1 X.2 Y.2 X.3 Y.3 X.4 Y.4 X.5 Y.5
1  A   1  16   4  19   7  22  10  25  13  28
2  B   2  17   5  20   8  23  11  26  14  29
3  C   3  18   6  21   9  24  12  27  15  30
20
Seth

data.table_1.9.5を使用すると、複数のvalue.var列を処理できるため、meltなしでこれを実行できます。 here からインストールできます

 library(data.table)
 dcast(setDT(my.df), ID~TIME, value.var=c('X', 'Y'))
 #   ID 1_X 2_X 3_X 4_X 5_X 1_Y 2_Y 3_Y 4_Y 5_Y
 #1:  A   1   4   7  10  13  16  19  22  25  28
 #2:  B   2   5   8  11  14  17  20  23  26  29
 #3:  C   3   6   9  12  15  18  21  24  27  30
14
akrun

注-2019年9月tidyr 内で、gather() + spread()アプローチ(この回答で説明)は、多かれ少なかれpivot_wider()アプローチに置き換えられました( ` この新しいtidyr回答 で説明されています)。遷移に関する現在の情報については、 ピボットビネット を参照してください。


tidyr パッケージを使用したソリューションは、基本的に reshapereshape2 を置き換えたものです。これらの2つのパッケージと同様に、最初にデータセットを長くし、次に幅を広くするという戦略です。

_library(magrittr); requireNamespace("tidyr"); requireNamespace("dplyr")
my.df %>%
  tidyr::gather(key=variable, value=value, c(X, Y)) %>%   # Make it even longer.
  dplyr::mutate(                                          # Create the spread key.
    time_by_variable   = paste0(variable, "_", TIME)
  ) %>%
  dplyr::select(ID, time_by_variable, value) %>%          # Retain these three.
  tidyr::spread(key=time_by_variable, value=value)        # Spread/widen.
_

tidyr::gather() 呼び出しの後の中間データセットは次のとおりです。

_ID TIME variable value
1   A    1        X     1
2   B    1        X     2
3   C    1        X     3
...
28  A    5        Y    28
29  B    5        Y    29
30  C    5        Y    30
_

最終的な結果は次のとおりです。

_  ID X_1 X_2 X_3 X_4 X_5 Y_1 Y_2 Y_3 Y_4 Y_5
1  A   1   4   7  10  13  16  19  22  25  28
2  B   2   5   8  11  14  17  20  23  26  29
3  C   3   6   9  12  15  18  21  24  27  30
_

tidyr::unite() は、@ JWillimanによって提案された代替手段です。これは、removeパラメーターがtrueの場合、機能的に上記の dplyr::mutate() および dplyr::select() の組み合わせと同等です(これがデフォルトです)。

このタイプの操作に慣れていない場合、tidyr::unite()は、覚えておく必要があるもう1つの関数であるため、小さな障害になる可能性があります。ただし、利点は、(a)より簡潔なコード(ie、4行が1つに置き換えられる)、および(b)変数名を繰り返す場所が少ない( iedplyr::select()句で変数を繰り返す/変更する必要はありません)。

_my.df %>%
  tidyr::gather(key=variable, value=value, c(X, Y)) %>%           # Make it even longer.
  tidyr::unite("time_by_variable", variable, TIME, remove=T) %>%  # Create the spread key `time_by_variable` while simultaneously dropping `variable` and `TIME`.
  tidyr::spread(key=time_by_variable, value=value)                # Spread/widen.
_
7
wibeasley

pivot_wider() 関数は tidyr の第2世代アプローチです(tidyr 1.0.0でリリース)。

_library(magrittr); requireNamespace("tidyr");

my.df %>%
  tidyr::pivot_wider(
    names_from  = c(TIME), # Can accommodate more variables, if needed.
    values_from = c(X, Y)
  )
_

結果

_# A tibble: 3 x 11
  ID      X_1   X_2   X_3   X_4   X_5   Y_1   Y_2   Y_3   Y_4   Y_5
  <fct> <int> <int> <int> <int> <int> <int> <int> <int> <int> <int>
1 A         1     4     7    10    13    16    19    22    25    28
2 B         2     5     8    11    14    17    20    23    26    29
3 C         3     6     9    12    15    18    21    24    27    30
_

これはおそらく 以前のtidyrアプローチ よりも望ましい(これは gather()spread() の組み合わせを使用します=)。

その他の機能は ピボットビネット で説明されています。この例は、目的の仕様が_id_cols_および_names_sep_のデフォルトと一致するため、特に簡潔です。

0
wibeasley