Devo usar um data.frame ou uma matriz?

152

Quando alguém deve usar a data.framee quando é melhor usar a matrix?

Ambos mantêm os dados em um formato retangular, portanto, às vezes, não está claro.

Existem regras gerais para quando usar qual tipo de dados?

micróbio
fonte
Geralmente, uma matriz pode ser mais adequada a um tipo específico de dados, mas se o pacote que você deseja usar para analisar a matriz espera um quadro de dados, você sempre precisará convertê-lo desnecessariamente. Eu acho que não há como evitar lembrar que pacote usa qual.
xApple 4/13

Respostas:

176

Parte da resposta já está contida na sua pergunta: Você usa quadros de dados se se espera que as colunas (variáveis) sejam de tipos diferentes (numérico / caractere / lógico etc.). Matrizes são para dados do mesmo tipo.

Conseqüentemente, a escolha matrix / data.frame é problemática apenas se você tiver dados do mesmo tipo.

A resposta depende do que você fará com os dados em data.frame / matrix. Se for passado para outras funções, o tipo esperado dos argumentos dessas funções determinará a escolha.

Além disso:

Matrizes são mais eficientes em memória:

m = matrix(1:4, 2, 2)
d = as.data.frame(m)
object.size(m)
# 216 bytes
object.size(d)
# 792 bytes

Matrizes são necessárias se você planeja executar operações do tipo álgebra linear.

Os quadros de dados são mais convenientes se você costuma consultar suas colunas por nome (por meio do operador compact $).

Os quadros de dados também são melhores em IMHO para relatar informações tabulares, pois você pode aplicar a formatação a cada coluna separadamente.

Michał
fonte
5
Uma coisa que eu acrescentaria a essa resposta é que, se você planeja usar o pacote ggplot2 para criar gráficos, o ggplot2 funciona apenas com data.frames e não com matrizes. Apenas algo para estar ciente!
Bajcz 28/03
77

Algo não mencionado por @Michal é que não apenas uma matriz é menor que o quadro de dados equivalente, o uso de matrizes pode tornar seu código muito mais eficiente do que o uso de quadros de dados, geralmente bastante. Essa é uma das razões pelas quais, internamente, muitas funções R serão coagidas a matrizes de dados que estão em quadros de dados.

Os quadros de dados geralmente são muito mais convenientes; nem sempre temos apenas pedaços atômicos de dados por aí.

Observe que você pode ter uma matriz de caracteres; você não precisa apenas ter dados numéricos para construir uma matriz em R.

Ao converter um quadro de dados em uma matriz, observe que existe uma data.matrix()função que lida com fatores adequadamente convertendo-os em valores numéricos com base nos níveis internos. Coagir via as.matrix()resultará em uma matriz de caracteres se algum dos rótulos de fator for não numérico. Comparar:

> head(as.matrix(data.frame(a = factor(letters), B = factor(LETTERS))))
     a   B  
[1,] "a" "A"
[2,] "b" "B"
[3,] "c" "C"
[4,] "d" "D"
[5,] "e" "E"
[6,] "f" "F"
> head(data.matrix(data.frame(a = factor(letters), B = factor(LETTERS))))
     a B
[1,] 1 1
[2,] 2 2
[3,] 3 3
[4,] 4 4
[5,] 5 5
[6,] 6 6

Eu quase sempre uso um quadro de dados para minhas tarefas de análise de dados, pois geralmente tenho mais do que apenas variáveis ​​numéricas. Quando codifico funções para pacotes, quase sempre coagir à matriz e depois formatar os resultados novamente como um quadro de dados. Isso ocorre porque os quadros de dados são convenientes.

Gavin Simpson
fonte
Eu estive pensando a diferença entre data.matrix () e as.matrix () também. Obrigado por esclarecê-los e suas dicas em programação.
microbe
Obrigado por compartilhar @Gavin Simpson! Você poderia apresentar um pouco mais sobre como voltar de 1-6 para af?
YJZ
1
@YZhang Você precisaria armazenar os rótulos para cada fator e um vetor lógico indicando quais colunas da matriz eram fatores. Seria relativamente trivial converter apenas as colunas que foram fatores novamente em fatores com os rótulos corretos. Os comentários não são bons locais para código, portanto, verifique se o Q já foi solicitado e respondido antes e se não fizer uma nova pergunta.
Gavin Simpson
47

@Michal: Matrizes não são realmente mais eficientes em termos de memória:

m <- matrix(1:400000, 200000, 2)
d <- data.frame(m)
object.size(m)
# 1600200 bytes
object.size(d)
# 1600776 bytes

... a menos que você tenha um grande número de colunas:

m <- matrix(1:400000, 2, 200000)
d <- data.frame(m)
object.size(m)
# 1600200 bytes
object.size(d)
# 22400568 bytes
petrelharp
fonte
o argumento da eficiência da memória é realmente data.framesoferecer mais flexibilidade sobre os tipos de coluna. data.frame(a = rnorm(1e6), b = sample(letters, 1e6, TRUE))será muito menor (6x pelo meu cálculo rápido) na memória do que a matrixversão por causa da coerção de tipo.
MichaelChirico
9

A matriz é na verdade um vetor com métodos adicionais. enquanto data.frame é uma lista. A diferença está no vetor versus lista. para eficiência de computação, fique com a matriz. Usando data.frame, se necessário.

user8341
fonte
3
Hmm, uma matriz é um vetor com dimensões, não vejo de onde os métodos entram?
Gavin Simpson
0

Matrizes e quadros de dados são matrizes 2D retangulares e podem ser heterogêneas por linhas e colunas . Eles compartilham alguns métodos e propriedades, mas não todos.

Exemplos:

M <- list(3.14,TRUE,5L,c(2,3,5),"dog",1i)  # a list
dim(M) <- c(2,3)                           # set dimensions
print(M)                                   # print result

#      [,1]  [,2]      [,3]
# [1,] 3.14  5         "dog"
# [2,] TRUE  Numeric,3 0+1i

DF <- data.frame(M)                   # a data frame
print(DF)                             # print result

#      X1      X2   X3
#  1 3.14       5  dog
#  2 TRUE 2, 3, 5 0+1i

M <- matrix(c(1,1,1,1,2,3,1,3,6),3)   # a numeric matrix
DF <- data.frame(M)                   # a all numeric data frame

solve(M)                              # obtains inverse matrix
solve(DF)                             # obtains inverse matrix
det(M)                                # obtains determinant
det(DF)                               # error
Trisquel
fonte
0

Não posso enfatizar mais a diferença de eficiência entre os dois! Embora seja verdade que os DFs sejam mais convenientes em alguns casos de análise de dados, eles também permitem dados heterogêneos e algumas bibliotecas os aceitam apenas, mas todos são realmente secundários, a menos que você escreva um código único para uma tarefa específica.

Deixe-me lhe dar um exemplo. Havia uma função que calculava o caminho 2D do método MCMC. Basicamente, isso significa que pegamos um ponto inicial (x, y) e iteramos um determinado algoritmo para encontrar um novo ponto (x, y) em cada etapa, construindo assim todo o caminho. O algoritmo envolve o cálculo de uma função bastante complexa e a geração de alguma variável aleatória a cada iteração; portanto, quando ele é executado por 12 segundos, eu acho que é bom, considerando a quantidade de coisas que ele faz em cada etapa. Dito isto, a função coletou todos os pontos no caminho construído juntamente com o valor de uma função objetivo em um data.frame de 3 colunas. Portanto, três colunas não são tão grandes e o número de etapas também foi superior a 10.000 razoáveis ​​(nesse tipo de problema, caminhos de comprimento 1.000.000 são típicos, portanto, 10.000 não é nada). Então, eu pensei em um DF 10, 000x3 definitivamente não é um problema. A razão pela qual um DF foi usado é simples. Após chamar a função, ggplot () foi chamado para desenhar o caminho (x, y) resultante. E o ggplot () não aceita uma matriz.

Então, em algum momento por curiosidade, decidi alterar a função para coletar o caminho em uma matriz. Felizmente, a sintaxe dos DFs e matrizes é semelhante, tudo o que fiz foi mudar a linha que especifica df como data.frame para uma inicializá-la como matriz. Aqui também preciso mencionar que, no código inicial, o DF foi inicializado para ter o tamanho final; portanto, mais tarde, no código da função, apenas novos valores foram registrados nos espaços já alocados e não havia sobrecarga de adicionar novas linhas ao DF. Isso torna a comparação ainda mais justa e também simplificou meu trabalho, pois não precisei reescrever mais nada na função. Apenas uma linha muda da alocação inicial de um data.frame do tamanho necessário para uma matriz do mesmo tamanho. Para adaptar a nova versão da função ao ggplot (), converti a matriz agora retornada em dados.

Depois de executar novamente o código, não pude acreditar no resultado. O código é executado em uma fração de segundo! Em vez de cerca de 12 segundos. E, novamente, a função durante as 10.000 iterações apenas lê e grava valores em espaços já alocados em um DF (e agora em uma matriz). E essa diferença também é para o tamanho razoável (ou melhor, pequeno) de 10000x3.

Portanto, se seu único motivo para usar um DF é torná-lo compatível com uma função de biblioteca como ggplot (), você sempre pode convertê-lo em um DF no último momento - trabalhe com matrizes o quanto achar conveniente. Se, por outro lado, houver uma razão mais substancial para usar um DF, como você usa algum pacote de análise de dados que exigiria uma transformação constante de matrizes para DFs e vice-versa, ou você mesmo não faz cálculos intensivos e usa apenas o padrão pacotes (muitos deles transformam internamente um DF em uma matriz, realizam seu trabalho e depois transformam o resultado de volta - para que toda a eficiência funcione para você) ou fazem um trabalho único para que você não se importe e sinta mais confortável com os DFs, não se preocupe com a eficiência.

Ou uma regra mais prática diferente: se você tiver uma pergunta como a do OP, use matrizes; portanto, você usaria DFs apenas quando não tiver essa pergunta (porque você já sabe que precisa usar DFs ou porque usa realmente não me importo, pois o código é único, etc.).

Mas, em geral, mantenha esse ponto de eficiência sempre em mente como uma prioridade.

Vadim
fonte