J'ai regardé autour de StackOverflow, mais je ne trouve pas de solution spécifique à mon problème, qui consiste à ajouter des lignes à un bloc de données R.
J'initialise une trame de données vide à 2 colonnes, comme suit.
df = data.frame(x = numeric(), y = character())
Ensuite, mon objectif est de parcourir une liste de valeurs et, à chaque itération, d'ajouter une valeur à la fin de la liste. J'ai commencé avec le code suivant.
for (i in 1:10) {
df$x = rbind(df$x, i)
df$y = rbind(df$y, toString(i))
}
J'ai aussi essayé les fonctions c
, append
et merge
sans succès. Veuillez me faire savoir si vous avez des suggestions.
Réponses:
Mettre à jour
Ne sachant pas ce que vous essayez de faire, je vais partager une autre suggestion: préallouer des vecteurs du type que vous souhaitez pour chaque colonne, insérer des valeurs dans ces vecteurs, puis, à la fin, créer votre fichier
data.frame
.Poursuivre avec Julian
f3
(une pré-allouéedata.frame
) comme l'option la plus rapide à ce jour, définie comme:Voici une approche similaire, mais dans laquelle le
data.frame
est créé à la dernière étape.microbenchmark
du package "microbenchmark" nous donnera un aperçu plus complet quesystem.time
:f1()
(l'approche ci-dessous) est incroyablement inefficace en raison de la fréquence d'appelsdata.frame
et du fait que la croissance des objets de cette façon est généralement lente dans R.f3()
est beaucoup améliorée en raison de la préallocation, mais ladata.frame
structure elle-même pourrait faire partie du goulot d'étranglement ici.f4()
essaie de contourner ce goulot d'étranglement sans compromettre l'approche que vous souhaitez adopter.Réponse originale
Ce n'est vraiment pas une bonne idée, mais si vous vouliez le faire de cette façon, je suppose que vous pouvez essayer:
Notez que dans votre code, il y a un autre problème:
stringsAsFactors
si vous souhaitez que les caractères ne soient pas convertis en facteurs. Utilisation:df = data.frame(x = numeric(), y = character(), stringsAsFactors = FALSE)
la source
data.frame
de la taille ultime attendue et d'ajouter les valeurs avec[
extraction / remplacement.Comparons les trois solutions proposées:
La meilleure solution consiste à pré-allouer de l'espace (comme prévu dans R). La meilleure solution suivante consiste à utiliser
list
, et la pire solution (au moins sur la base de ces résultats de synchronisation) semble êtrerbind
.la source
df <- rbind(df, data.frame(x = i, y = toString(i)))
Supposons que vous ne connaissiez tout simplement pas la taille du data.frame à l'avance. Il peut s'agir de quelques lignes ou de quelques millions. Vous devez avoir une sorte de conteneur, qui se développe dynamiquement. En tenant compte de mon expérience et de toutes les réponses connexes en SO, je viens avec 4 solutions distinctes:
rbindlist
au data.frameUtilisez
data.table
leset
fonctionnement rapide de et associez-le au doublement manuel de la table en cas de besoin.Utilisez
RSQLite
et ajoutez au tableau conservé en mémoire.data.frame
La propre capacité de développer et d'utiliser un environnement personnalisé (qui a une sémantique de référence) pour stocker le data.frame afin qu'il ne soit pas copié au retour.Voici un test de toutes les méthodes pour le petit et le grand nombre de lignes ajoutées. Chaque méthode est associée à 3 fonctions:
create(first_element)
qui renvoie l'objet de support approprié avecfirst_element
put in.append(object, element)
qui ajoute leelement
à la fin du tableau (représenté parobject
).access(object)
obtient ledata.frame
avec tous les éléments insérés.rbindlist
au data.frameC'est assez simple et simple:
data.table::set
+ doubler manuellement la table en cas de besoin.Je vais stocker la vraie longueur de la table dans un
rowcount
attribut.SQL devrait être optimisé pour une insertion rapide des enregistrements, donc j'avais au départ de grands espoirs de
RSQLite
solutionIl s'agit essentiellement d'un copier-coller de la réponse de Karsten W. sur un fil similaire.
data.frame
propre environnement personnalisé d'ajout de lignes.La suite de tests:
Pour plus de commodité, j'utiliserai une fonction de test pour les couvrir tous avec un appel indirect. (J'ai vérifié: utiliser
do.call
au lieu d'appeler directement les fonctions ne rend pas le code mesurable plus longtemps).Voyons les performances pour n = 10 insertions.
J'ai également ajouté des fonctions «placebo» (avec suffixe
0
) qui n'effectuent rien - juste pour mesurer la surcharge de la configuration de test.Pour les lignes 1E5 (mesures effectuées sur un processeur Intel (R) Core (TM) i7-4710HQ à 2,50 GHz):
Il semble que la solution basée sur SQLite, bien qu'elle regagne un peu de vitesse sur des données volumineuses, est loin d'être proche de data.table + croissance exponentielle manuelle. La différence est de près de deux ordres de grandeur!
Résumé
Si vous savez que vous allez ajouter un nombre assez petit de lignes (n <= 100), allez-y et utilisez la solution la plus simple possible: affectez simplement les lignes au data.frame en utilisant la notation entre crochets et ignorez le fait que le data.frame est non prérempli.
Pour tout le reste, utilisez
data.table::set
et développez la table data.table de manière exponentielle (par exemple en utilisant mon code).la source
Mise à jour avec Purrr, Tidyr & Dplyr
Comme la question est déjà datée (6 ans), les réponses manquent une solution avec les nouveaux packages tidyr et purrr. Donc, pour les personnes travaillant avec ces packages, je souhaite ajouter une solution aux réponses précédentes - toutes très intéressantes, en particulier.
Le plus grand avantage de purrr et tidyr est une meilleure lisibilité à mon humble avis. purrr remplace lapply par la famille map () plus flexible, tidyr propose la méthode super intuitive add_row - fait juste ce qu'elle dit :)
Cette solution est courte et intuitive à lire, et elle est relativement rapide:
Il évolue presque linéairement, donc pour 1e5 lignes, les performances sont:
ce qui le placerait au deuxième rang juste après data.table (si vous ignorez le placebo) dans le benchmark de @Adam Ryczkowski:
la source
add_row
. Par exemple:map_dfr(1:1e5, function(x) { tibble(x = x, y = toString(x)) })
.bind_rows(df, map_dfr(1:1e5, function(x) { tibble(x = x, y = toString(x)) }))
au lieu d'utiliseradd_row
.Prenons un vecteur 'point' qui a des nombres de 1 à 5
point = c(1,2,3,4,5)
si nous voulons ajouter un numéro 6 n'importe où dans le vecteur, la commande ci-dessous peut être utile
i) Vecteurs
new_var = append(point, 6 ,after = length(point))
ii) colonnes d'un tableau
new_var = append(point, 6 ,after = length(mtcars$mpg))
La commande
append
prend trois arguments:Facile...!! Toutes mes excuses en cas de ...!
la source
Une solution plus générique pour pourrait être la suivante.
La fonction extendDf () étend un bloc de données avec n lignes.
Par exemple:
la source
Ma solution est presque la même que la réponse originale mais cela ne fonctionne pas pour moi.
Alors, j'ai donné des noms aux colonnes et ça marche:
la source