web-dev-qa-db-fra.com

Création d'un fichier de données R, ligne par ligne

Je voudrais construire un cadre de données rangée par rangée dans R. J'ai fait quelques recherches et tout ce que j'ai suggéré, c'est de créer une liste vide, de conserver une liste index scalaire, puis d'ajouter chaque fois à la liste. une base de données à une seule ligne et avance l’index de la liste d’une unité. Enfin, do.call(rbind,) sur la liste.

Bien que cela fonctionne, cela semble très lourd. N'y a-t-il pas un moyen plus facile d'atteindre le même objectif?

Évidemment, je me réfère aux cas où je ne peux pas utiliser une fonction apply et que je dois explicitement créer le fichier image par ligne. Au moins, y a-t-il un moyen de Push à la fin d'une liste au lieu de garder explicitement trace du dernier index utilisé?

98
David B

Vous pouvez les agrandir ligne par ligne en ajoutant ou en utilisant rbind()

Cela ne signifie pas que vous devriez. La croissance dynamique des structures est l’un des moyens les moins efficaces de coder en R.

Si vous le pouvez, allouez l'intégralité de votre data.frame:

N <- 1e4  # total number of rows to preallocate--possibly an overestimate

DF <- data.frame(num=rep(NA, N), txt=rep("", N),  # as many cols as you need
                 stringsAsFactors=FALSE)          # you don't know levels yet

puis pendant vos opérations insérer une ligne à la fois

DF[i, ] <- list(1.4, "foo")

Cela devrait fonctionner pour data.frame arbitraire et être beaucoup plus efficace. Si vous avez dépassé N, vous pouvez toujours réduire les lignes vides à la fin.

88
Dirk Eddelbuettel

On peut ajouter des lignes à NULL:

df<-NULL;
while(...){
  #Some code that generates new row
  rbind(df,row)->df
}

par exemple

df<-NULL
for(e in 1:10) rbind(df,data.frame(x=e,square=e^2,even=factor(e%%2==0)))->df
print(df)
46
mbq

Ceci est un exemple stupide de l'utilisation de do.call(rbind,) sur la sortie de Map() [qui est similaire à lapply()]

> DF <- do.call(rbind,Map(function(x) data.frame(a=x,b=x+1),x=1:3))
> DF
  x y
1 1 2
2 2 3
3 3 4
> class(DF)
[1] "data.frame"

J'utilise cette construction assez souvent.

9
hatmatrix

La raison pour laquelle j'aime tant Rcpp est que je ne comprends pas toujours ce que pense R Core et, avec Rcpp, le plus souvent, je n'ai pas à le faire. 

En termes philosophiques, vous êtes dans un état de péché en ce qui concerne le paradigme fonctionnel, qui essaie de garantir que chaque valeur apparaît / indépendante de toute autre valeur; changer une valeur ne devrait jamais causer de changement visible dans une autre valeur, comme vous le feriez avec des pointeurs partageant la représentation en C. 

Les problèmes se posent lorsque la programmation fonctionnelle signale au petit bateau de s'éloigner de celui-ci et que celui-ci répond "Je suis un phare". Effectuer une longue série de petites modifications sur un objet volumineux que vous souhaitez traiter entre-temps vous place au centre des phares. 

Dans la STL C++, Push_back() est un mode de vie. Il n'essaie pas d'être fonctionnel, mais il essaye de prendre en charge les idiomes de programmation courants efficacement

Avec une certaine ingéniosité dans les coulisses, vous pouvez parfois vous organiser pour avoir un pied dans chaque monde. Les systèmes de fichiers basés sur des instantanés en sont un bon exemple (ils ont évolué à partir de concepts tels que les montages d'union, qui s'appliquent également des deux côtés). 

Si R Core souhaitait le faire, le stockage de vecteur sous-jacent pourrait fonctionner comme un montage en union. Une référence à la mémoire de vecteurs peut être valide pour les indices 1:N, tandis qu'une autre référence à la même mémoire est valide pour les indices 1:(N+1). Il peut y avoir un espace de stockage réservé qui n’est pas encore référencé de manière valable par tout sauf pratique pour une rapide Push_back(). Vous ne violez pas le concept fonctionnel lors de l'ajout en dehors de la plage considérée comme valide par toute référence existante. 

En ajoutant éventuellement des lignes de manière incrémentielle, vous n’êtes plus en réserve. Vous aurez besoin de créer de nouvelles copies de tout, avec le stockage multiplié par un incrément. Les implémentations STL que j'ai utilisées ont tendance à multiplier par 2 le stockage lors de l'allongement de l'allocation. Je pensais avoir lu dans R Internals qu'il existait une structure de mémoire dans laquelle le stockage augmentait de 20%. Dans les deux cas, les opérations de croissance ont lieu à une fréquence logarithmique par rapport au nombre total d'éléments ajoutés. Sur une base amortie, cela est généralement acceptable. 

Au fur et à mesure que les astuces se cachent, j'ai vu pire. Chaque fois que vous Push_back() une nouvelle ligne dans la structure de données, une structure d'index de niveau supérieur doit être copiée. La nouvelle ligne peut s’ajouter à la représentation partagée sans affecter les anciennes valeurs fonctionnelles. Je ne pense même pas que cela compliquerait beaucoup le ramasse-miettes; Puisque je ne propose pas Push_front(), toutes les références sont des références de préfixe au début du stockage de vecteur alloué. 

8
Allan Stokes

La réponse de Dirk Eddelbuettel est la meilleure; Ici, je remarque juste que vous pouvez vous en tirer en ne spécifiant pas à l'avance les dimensions ou types de données de la structure de données, ce qui est parfois utile si vous avez plusieurs types de données et de nombreuses colonnes:

row1<-list("a",1,FALSE) #use 'list', not 'c' or 'cbind'!
row2<-list("b",2,TRUE)  

df<-data.frame(row1,stringsAsFactors = F) #first row
df<-rbind(d,row2) #now this works as you'd expect.
1
John

Si vous avez des vecteurs destinés à devenir des lignes, concaténez-les à l'aide de c(), transmettez-les à une matrice, ligne par ligne, et convertissez cette matrice en une image.

Par exemple, des lignes

dummydata1=c(2002,10,1,12.00,101,426340.0,4411238.0,3598.0,0.92,57.77,4.80,238.29,-9.9)
dummydata2=c(2002,10,2,12.00,101,426340.0,4411238.0,3598.0,-3.02,78.77,-9999.00,-99.0,-9.9)
dummydata3=c(2002,10,8,12.00,101,426340.0,4411238.0,3598.0,-5.02,88.77,-9999.00,-99.0,-9.9)

peut être converti en une trame de données ainsi:

dummyset=c(dummydata1,dummydata2,dummydata3)
col.len=length(dummydata1)
dummytable=data.frame(matrix(data=dummyset,ncol=col.len,byrow=TRUE))

Certes, je vois deux limitations majeures: (1) cela ne fonctionne qu'avec les données monomodes, et (2) vous devez connaître le nombre final de colonnes pour que cela fonctionne (c.-à-d. tableau irrégulier dont la plus grande longueur de ligne est inconnue a priori ).

Cette solution semble simple, mais de mon expérience avec les conversions de types dans R, je suis sûre que cela crée de nouveaux défis en aval. Quelqu'un peut-il commenter cela?

0
Keegan Smith

J'ai trouvé cette façon de créer des données par image brute sans matrice.

Avec nom de colonne automatique

df<-data.frame(
        t(data.frame(c(1,"a",100),c(2,"b",200),c(3,"c",300)))
        ,row.names = NULL,stringsAsFactors = FALSE
    )

Avec nom de colonne

df<-setNames(
        data.frame(
            t(data.frame(c(1,"a",100),c(2,"b",200),c(3,"c",300)))
            ,row.names = NULL,stringsAsFactors = FALSE
        ), 
        c("col1","col2","col3")
    )
0
phili_b