Dois-je utiliser un data.frame ou une matrice?


152

Quand faut-il utiliser un data.frame, et quand est-il préférable d'utiliser un matrix?

Les deux conservent les données dans un format rectangulaire, donc parfois ce n'est pas clair.

Existe-t-il des règles générales pour savoir quand utiliser quel type de données?


Souvent, une matrice peut être mieux adaptée à un type particulier de données, mais si le package que vous souhaitez utiliser pour analyser ladite matrice attend une trame de données, vous devrez toujours la convertir inutilement. Je pense qu'il n'y a aucun moyen d'éviter de se souvenir quel paquet utilise lequel.
xApple

Réponses:


176

Une partie de la réponse est déjà contenue dans votre question: Vous utilisez des blocs de données si l'on peut s'attendre à ce que les colonnes (variables) soient de types différents (numérique / caractère / logique, etc.). Les matrices sont destinées aux données du même type.

Par conséquent, le choix matrice / data.frame n'est problématique que si vous avez des données du même type.

La réponse dépend de ce que vous allez faire des données dans data.frame / matrix. S'il doit être passé à d'autres fonctions, le type attendu des arguments de ces fonctions détermine le choix.

Aussi:

Les matrices sont plus efficaces en mémoire:

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

Les matrices sont une nécessité si vous prévoyez d'effectuer des opérations de type algèbre linéaire.

Les blocs de données sont plus pratiques si vous faites fréquemment référence à ses colonnes par leur nom (via l'opérateur compact $).

Les cadres de données sont également meilleurs à mon humble avis pour le reporting (impression) des informations tabulaires car vous pouvez appliquer le formatage à chaque colonne séparément.


5
Une chose que j'ajouterais à cette réponse est que si vous prévoyez d'utiliser le package ggplot2 pour créer des graphiques, ggplot2 ne fonctionne qu'avec data.frames et non avec des matrices. Juste quelque chose à savoir!
Bajcz

77

Quelque chose qui n'a pas été mentionné par @Michal est que non seulement une matrice est plus petite que la trame de données équivalente, l'utilisation de matrices peut rendre votre code beaucoup plus efficace que l'utilisation de trames de données, souvent considérablement. C'est l'une des raisons pour lesquelles en interne, de nombreuses fonctions R contraindront à des matrices de données qui sont dans des trames de données.

Les trames de données sont souvent beaucoup plus pratiques; on n'a pas toujours uniquement des morceaux atomiques de données qui traînent.

Notez que vous pouvez avoir une matrice de caractères; vous n'avez pas seulement besoin de données numériques pour construire une matrice dans R.

Lors de la conversion d'une trame de données en matrice, notez qu'il existe une data.matrix()fonction qui gère les facteurs de manière appropriée en les convertissant en valeurs numériques basées sur les niveaux internes. Le fait de forcer via as.matrix()produira une matrice de caractères si l'une des étiquettes de facteur n'est pas numérique. Comparer:

> 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

J'utilise presque toujours une base de données pour mes tâches d'analyse de données car j'ai souvent plus que de simples variables numériques. Lorsque je code des fonctions pour des packages, je force presque toujours à matricer puis à formater les résultats sous forme de trame de données. C'est parce que les trames de données sont pratiques.


Je me suis également demandé la différence entre data.matrix () et as.matrix (). Merci de les clarifier et de vos conseils en programmation.
microbe

Merci d'avoir partagé @Gavin Simpson! Pourriez-vous nous en dire plus sur la façon de revenir de 1-6 à af?
YJZ

1
@YZhang Vous auriez besoin de stocker les étiquettes pour chaque facteur et un vecteur logique indiquant quelles colonnes de la matrice étaient des facteurs. Ensuite, il serait relativement simple de convertir uniquement les colonnes qui étaient des facteurs en facteurs avec les étiquettes correctes. Les commentaires ne sont pas de bons endroits pour le code, alors voyez si le Q a déjà été posé et répondu et si ce n'est pas le cas, posez une nouvelle question.
Gavin Simpson

47

@Michal: Les matrices ne sont pas vraiment plus efficaces en mémoire:

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

... sauf si vous avez un grand nombre de colonnes:

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

L'argument de l'efficacité de la mémoire consiste en fait à data.framesoffrir plus de flexibilité sur les types de colonnes. data.frame(a = rnorm(1e6), b = sample(letters, 1e6, TRUE))sera beaucoup plus petit (6x selon mon calcul rapide) en mémoire que la matrixversion en raison de la coercition de type.
MichaelChirico

9

La matrice est en fait un vecteur avec des méthodes supplémentaires. tandis que data.frame est une liste. La différence est due au vecteur vs liste. pour l'efficacité du calcul, tenez-vous-en à la matrice. Utiliser data.frame si vous le devez.


3
Hmm, une matrice est un vecteur avec des dimensions, je ne vois pas où les méthodes entrent en jeu?
Gavin Simpson

0

Les matrices et les blocs de données sont des tableaux 2D rectangulaires et peuvent être hétérogènes par lignes et colonnes . Ils partagent certaines méthodes et propriétés, mais pas toutes.

Exemples:

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

0

Je ne saurais trop insister sur la différence d'efficacité entre les deux! S'il est vrai que les DF sont plus pratiques dans certains cas d'analyse de données en particulier, ils permettent également des données hétérogènes, et certaines bibliothèques les acceptent uniquement, tout cela est vraiment secondaire à moins que vous n'écriviez un code unique pour une tâche spécifique.

Laisse moi te donner un exemple. Il y avait une fonction qui calculait le chemin 2D de la méthode MCMC. Fondamentalement, cela signifie que nous prenons un point initial (x, y), et itérons un certain algorithme pour trouver un nouveau point (x, y) à chaque étape, construisant de cette façon tout le chemin. L'algorithme implique le calcul d'une fonction assez complexe et la génération d'une variable aléatoire à chaque itération, donc quand il s'exécute pendant 12 secondes, j'ai pensé que c'était bien compte tenu de la quantité de choses qu'il fait à chaque étape. Cela étant dit, la fonction a collecté tous les points du chemin construit avec la valeur d'une fonction objectif dans un data.frame à 3 colonnes. Donc, 3 colonnes n'est pas si grand, et le nombre d'étapes était également plus que raisonnable 10 000 (dans ce genre de problèmes, les chemins d'une longueur de 1 000 000 sont typiques, donc 10 000 n'est rien). Alors, j'ai pensé à un DF 10, 000x3 n'est certainement pas un problème. La raison pour laquelle un DF a été utilisé est simple. Après avoir appelé la fonction, ggplot () a été appelé pour dessiner le chemin (x, y) résultant. Et ggplot () n'accepte pas de matrice.

Puis, à un moment donné par curiosité, j'ai décidé de changer la fonction pour collecter le chemin dans une matrice. Heureusement, la syntaxe des DF et des matrices est similaire, tout ce que j'ai fait a été de changer la ligne spécifiant df comme data.frame en l'initialisant en tant que matrice. Ici, je dois également mentionner que dans le code initial, le DF a été initialisé pour avoir la taille finale, donc plus tard dans le code de la fonction, seules les nouvelles valeurs ont été enregistrées dans des espaces déjà alloués, et il n'y a pas eu de surcharge pour ajouter de nouvelles lignes au DF. Cela rend la comparaison encore plus juste, et cela a également simplifié mon travail car je n'avais pas besoin de réécrire davantage dans la fonction. Une seule ligne change de l'allocation initiale d'un data.frame de la taille requise à une matrice de la même taille. Pour adapter la nouvelle version de la fonction à ggplot (), j'ai converti la matrice maintenant renvoyée en données.

Après avoir réexécuté le code, je ne pouvais pas croire le résultat. Le code s'exécute en une fraction de seconde! Au lieu d'environ 12 secondes. Et encore une fois, la fonction pendant les 10 000 itérations n'a lu et écrit des valeurs que dans des espaces déjà alloués dans un DF (et maintenant dans une matrice). Et cette différence concerne également la taille raisonnable (ou plutôt petite) de 10000x3.

Donc, si votre seule raison d'utiliser un DF est de le rendre compatible avec une fonction de bibliothèque telle que ggplot (), vous pouvez toujours le convertir en DF au dernier moment - travaillez avec des matrices autant que vous le souhaitez. Si, d'un autre côté, il existe une raison plus importante d'utiliser un DF, par exemple si vous utilisez un logiciel d'analyse de données qui nécessiterait une transformation constante des matrices en DF et inversement, ou si vous ne faites pas de calculs intensifs vous-même et utilisez uniquement le standard paquets (beaucoup d'entre eux transforment en fait un DF en matrice, font leur travail, puis transforment le résultat - ils font donc tout le travail d'efficacité pour vous), ou font un travail ponctuel pour que vous ne vous en souciez pas et ne vous sentiez pas plus à l'aise avec les DF, alors vous ne devriez pas vous soucier de l'efficacité.

Ou une autre règle plus pratique: si vous avez une question comme dans l'OP, utilisez des matrices, vous n'utiliserez donc les DF que lorsque vous n'avez pas une telle question (parce que vous savez déjà que vous devez utiliser des DF, ou parce que vous le faites pas vraiment de soins car le code est unique, etc.).

Mais en général, gardez toujours ce point d'efficacité à l'esprit en tant que priorité.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.