Przekształć trzy kolumny ramki danych do macierzy ("długi" do "Szeroki" format) [duplikat]
Mam data.frame
, który wygląda tak.
x a 1
x b 2
x c 3
y a 3
y b 3
y c 2
Chcę to w postaci matrycy, więc mogę go nakarmić heatmap, aby stworzyć wykres. Wynik powinien wyglądać mniej więcej tak:
a b c
x 1 2 3
y 3 3 2
Próbowałem cast
z pakietu reshape i próbowałem napisać ręczną funkcję, aby to zrobić, ale wydaje się, że nie jestem w stanie zrobić tego dobrze.
6 answers
Jest na to wiele sposobów. Ta odpowiedź zaczyna się od tego, co szybko staje się standardową metodą, ale obejmuje również starsze metody i różne inne metody od odpowiedzi na podobne pytania rozsiane po tej stronie.
tmp <- data.frame(x=gl(2,3, labels=letters[24:25]),
y=gl(3,1,6, labels=letters[1:3]),
z=c(1,2,3,3,3,2))
Korzystanie z tidyverse:
Nowym fajnym sposobem na to jest pivot_wider
z tidyr 1.0.0
. Zwraca ramkę danych, która prawdopodobnie jest tym, czego oczekuje większość czytelników tej odpowiedzi. W przypadku heatmapy musisz jednak przekonwertować tę do prawdziwej matrycy.
library(tidyr)
pivot_wider(tmp, names_from = y, values_from = z)
## # A tibble: 2 x 4
## x a b c
## <fct> <dbl> <dbl> <dbl>
## 1 x 1 2 3
## 2 y 3 3 2
Stary fajny nowy sposób na to jest z spread
z tidyr
. Podobnie zwraca ramkę danych.
library(tidyr)
spread(tmp, y, z)
## x a b c
## 1 x 1 2 3
## 2 y 3 3 2
Using reshape2 :
Jednym z pierwszych kroków w kierunku tidyverse był pakiet reshape2.
Aby uzyskać macierz użyj acast
:
library(reshape2)
acast(tmp, x~y, value.var="z")
## a b c
## x 1 2 3
## y 3 3 2
Lub aby uzyskać ramkę danych, użyj dcast
, Jak tutaj: Przekształć dane dla wartości w jednej kolumnie.
dcast(tmp, x~y, value.var="z")
## x a b c
## 1 x 1 2 3
## 2 y 3 3 2
Użycie plyr :
In between reshape2 i przyszedł tidyverse plyr
, z funkcją daply
, Jak pokazano tutaj: https://stackoverflow.com/a/7020101/210673
library(plyr)
daply(tmp, .(x, y), function(x) x$z)
## y
## x a b c
## x 1 2 3
## y 3 3 2
Korzystanie z indeksowania macierzy:
Jest to trochę stara szkoła, ale jest miłą demonstracją indeksowania macierzy, która może być naprawdę przydatna w pewnych sytuacjach.
with(tmp, {
out <- matrix(nrow=nlevels(x), ncol=nlevels(y),
dimnames=list(levels(x), levels(y)))
out[cbind(x, y)] <- z
out
})
Za pomocą xtabs
:
xtabs(z~x+y, data=tmp)
Korzystanie z rzadkiej macierzy:
Istnieje również sparseMatrix
w Matrix
pakiet, jak widać tutaj: R-konwersja dużej tabeli na macierz przez nazwy kolumn
with(tmp, sparseMatrix(i = as.numeric(x), j=as.numeric(y), x=z,
dimnames=list(levels(x), levels(y))))
## 2 x 3 sparse Matrix of class "dgCMatrix"
## a b c
## x 1 2 3
## y 3 3 2
Za pomocą reshape
:
Możesz również użyć podstawowej funkcji R reshape
, jak sugerowano tutaj: Konwertuj tabelę na macierz według nazw kolumn , chociaż musisz potem trochę manipulować, aby usunąć dodatkowe kolumny i uzyskać poprawne nazwy (nie pokazane).
reshape(tmp, idvar="x", timevar="y", direction="wide")
## x z.a z.b z.c
## 1 x 1 2 3
## 4 y 3 3 2
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-10-14 15:51:51
Baza R, unstack
unstack(df, V3 ~ V2)
# a b c
# 1 1 2 3
# 2 3 3 2
To może nie być ogólne rozwiązanie, ale działa dobrze w tym przypadku.
Dane
df<-structure(list(V1 = structure(c(1L, 1L, 1L, 2L, 2L, 2L), .Label = c("x",
"y"), class = "factor"), V2 = structure(c(1L, 2L, 3L, 1L, 2L,
3L), .Label = c("a", "b", "c"), class = "factor"), V3 = c(1L,
2L, 3L, 3L, 3L, 2L)), .Names = c("V1", "V2", "V3"), class = "data.frame", row.names = c(NA,
-6L))
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-10-05 20:11:50
Pytanie ma kilka lat, ale być może niektórzy ludzie nadal są zainteresowani alternatywnymi odpowiedziami.
Jeśli nie chcesz ładować żadnych pakietów, możesz użyć tej funkcji:
#' Converts three columns of a data.frame into a matrix -- e.g. to plot
#' the data via image() later on. Two of the columns form the row and
#' col dimensions of the matrix. The third column provides values for
#' the matrix.
#'
#' @param data data.frame: input data
#' @param rowtitle string: row-dimension; name of the column in data, which distinct values should be used as row names in the output matrix
#' @param coltitle string: col-dimension; name of the column in data, which distinct values should be used as column names in the output matrix
#' @param datatitle string: name of the column in data, which values should be filled into the output matrix
#' @param rowdecreasing logical: should the row names be in ascending (FALSE) or in descending (TRUE) order?
#' @param coldecreasing logical: should the col names be in ascending (FALSE) or in descending (TRUE) order?
#' @param default_value numeric: default value of matrix entries if no value exists in data.frame for the entries
#' @return matrix: matrix containing values of data[[datatitle]] with rownames data[[rowtitle]] and colnames data[coltitle]
#' @author Daniel Neumann
#' @date 2017-08-29
data.frame2matrix = function(data, rowtitle, coltitle, datatitle,
rowdecreasing = FALSE, coldecreasing = FALSE,
default_value = NA) {
# check, whether titles exist as columns names in the data.frame data
if ( (!(rowtitle%in%names(data)))
|| (!(coltitle%in%names(data)))
|| (!(datatitle%in%names(data))) ) {
stop('data.frame2matrix: bad row-, col-, or datatitle.')
}
# get number of rows in data
ndata = dim(data)[1]
# extract rownames and colnames for the matrix from the data.frame
rownames = sort(unique(data[[rowtitle]]), decreasing = rowdecreasing)
nrows = length(rownames)
colnames = sort(unique(data[[coltitle]]), decreasing = coldecreasing)
ncols = length(colnames)
# initialize the matrix
out_matrix = matrix(NA,
nrow = nrows, ncol = ncols,
dimnames=list(rownames, colnames))
# iterate rows of data
for (i1 in 1:ndata) {
# get matrix-row and matrix-column indices for the current data-row
iR = which(rownames==data[[rowtitle]][i1])
iC = which(colnames==data[[coltitle]][i1])
# throw an error if the matrix entry (iR,iC) is already filled.
if (!is.na(out_matrix[iR, iC])) stop('data.frame2matrix: double entry in data.frame')
out_matrix[iR, iC] = data[[datatitle]][i1]
}
# set empty matrix entries to the default value
out_matrix[is.na(out_matrix)] = default_value
# return matrix
return(out_matrix)
}
Jak to działa:
myData = as.data.frame(list('dim1'=c('x', 'x', 'x', 'y','y','y'),
'dim2'=c('a','b','c','a','b','c'),
'values'=c(1,2,3,3,3,2)))
myMatrix = data.frame2matrix(myData, 'dim1', 'dim2', 'values')
myMatrix
> a b c
> x 1 2 3
> y 3 3 2
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-29 18:29:16
Ze względu na kompletność, istnieje tapply()
rozwiązanie wokół.
with(d, tapply(z, list(x, y), sum))
# a b c
# x 1 2 3
# y 3 3 2
Dane
d <- structure(list(x = structure(c(1L, 1L, 1L, 2L, 2L, 2L), .Label = c("x",
"y"), class = "factor"), y = structure(c(1L, 2L, 3L, 1L, 2L,
3L), .Label = c("a", "b", "c"), class = "factor"), z = c(1, 2,
3, 3, 3, 2)), class = "data.frame", row.names = c(NA, -6L))
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-02-01 01:12:32
Z tidyr 0.8.3.9000
została wprowadzona nowa funkcja o nazwie pivot_wider()
. Jest to w zasadzie ulepszona wersja poprzedniej funkcji spread()
(która ponadto nie jest już w fazie aktywnego rozwoju) . Z winieta obrotowa:
Ta winieta opisuje użycie nowego pivot_longer () i funkcje pivot_wider (). Ich celem jest poprawa użyteczności gather() i spread () oraz zawierają najnowocześniejsze znalezione funkcje w innych opakowaniach.
Dla jakiś czas, było oczywiste, że jest coś zasadniczo błąd w konstrukcji spread () i gather (). Wielu ludzi nie znajduje nazwy intuicyjne i trudno zapamiętać, w którym kierunku odpowiada rozprzestrzenianiu, a które gromadzeniu. Wydaje się, że zaskakująco trudno zapamiętać argumenty tych funkcji, co oznacza, że wiele osób(w tym ja!) muszą skonsultować się z dokumentacja za każdym razem.
Jak z niego korzystać (korzystając z danych z @ Aaron):
pivot_wider(data = tmp, names_from = y, values_from = z)
x a b c
<fct> <dbl> <dbl> <dbl>
1 x 1 2 3
2 y 3 3 2
Lub w" pełnym " tidyverse
fasonie:
tmp %>%
pivot_wider(names_from = y, values_from = z)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-09-16 18:51:56
Pakiet tidyr z tidyverse ma doskonałą funkcję, która to robi.
Zakładając, że Twoje zmienne mają nazwy v1, v2 i v3, od lewej do prawej, a ramka danych ma nazwę dat:
dat %>%
spread(key = v2,
value = v3)
Ta da!Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-12 19:38:19