web-dev-qa-db-fra.com

Ajout d'une colonne à un data.frame

J'ai le data.frame ci-dessous. Je souhaite ajouter une colonne qui classe mes données en fonction de la colonne 1 (h_no), de sorte que la première série de h_no 1,2,3,4 correspond à la classe 1, la deuxième série de h_no (1 à 7) correspond à la classe 2 etc. comme indiqué dans la dernière colonne.

h_no  h_freq  h_freqsq
1     0.09091 0.008264628 1
2     0.00000 0.000000000 1
3     0.04545 0.002065702 1
4     0.00000 0.000000000 1  
1     0.13636 0.018594050 2
2     0.00000 0.000000000 2
3     0.00000 0.000000000 2
4     0.04545 0.002065702 2
5     0.31818 0.101238512 2
6     0.00000 0.000000000 2
7     0.50000 0.250000000 2 
1     0.13636 0.018594050 3 
2     0.09091 0.008264628 3
3     0.40909 0.167354628 3
4     0.04545 0.002065702 3
102

Vous pouvez ajouter une colonne à vos données en utilisant diverses techniques. Les guillemets ci-dessous proviennent de la section "Détails" du texte d'aide correspondant, [[.data.frame.

Les trames de données peuvent être indexées selon plusieurs modes. Lorsque [ et [[ sont utilisés avec un seul index vectoriel (x[i] ou x[[i]]), ils indexent le bloc de données comme s'il s'agissait d'une liste.

my.dataframe["new.col"] <- a.vector
my.dataframe[["new.col"]] <- a.vector

La méthode data.frame pour $ traite x comme une liste

my.dataframe$new.col <- a.vector

Lorsque [ et [[ sont utilisés avec deux index (x[i, j] et x[[i, j]]), ils agissent comme une indexation d'une matrice.

my.dataframe[ , "new.col"] <- a.vector

Puisque la méthode pour data.frame suppose que si vous ne spécifiez pas si vous travaillez avec des colonnes ou des lignes, cela supposera que vous entendez des colonnes.


Pour votre exemple, cela devrait fonctionner:

# make some fake data
your.df <- data.frame(no = c(1:4, 1:7, 1:5), h_freq = runif(16), h_freqsq = runif(16))

# find where one appears and 
from <- which(your.df$no == 1)
to <- c((from-1)[-1], nrow(your.df)) # up to which point the sequence runs

# generate a sequence (len) and based on its length, repeat a consecutive number len times
get.seq <- mapply(from, to, 1:length(from), FUN = function(x, y, z) {
            len <- length(seq(from = x[1], to = y[1]))
            return(rep(z, times = len))
         })

# when we unlist, we get a vector
your.df$group <- unlist(get.seq)
# and append it to your original data.frame. since this is
# designating a group, it makes sense to make it a factor
your.df$group <- as.factor(your.df$group)


   no     h_freq   h_freqsq group
1   1 0.40998238 0.06463876     1
2   2 0.98086928 0.33093795     1
3   3 0.28908651 0.74077119     1
4   4 0.10476768 0.56784786     1
5   1 0.75478995 0.60479945     2
6   2 0.26974011 0.95231761     2
7   3 0.53676266 0.74370154     2
8   4 0.99784066 0.37499294     2
9   5 0.89771767 0.83467805     2
10  6 0.05363139 0.32066178     2
11  7 0.71741529 0.84572717     2
12  1 0.10654430 0.32917711     3
13  2 0.41971959 0.87155514     3
14  3 0.32432646 0.65789294     3
15  4 0.77896780 0.27599187     3
16  5 0.06100008 0.55399326     3
143
Roman Luštrik

Facilement: votre bloc de données est un

b <- A[,1]
b <- b==1
b <- cumsum(b)

Ensuite, vous obtenez la colonne b. 

10
user1333396

Si je comprends bien la question, vous souhaitez détecter si le h_no n'augmente pas, puis incrémenter la class. (Je vais expliquer comment j'ai résolu ce problème, il y a une fonction autonome à la fin.)

Travail

Nous ne nous intéressons qu'à la colonne h_no pour le moment, nous pouvons donc extraire cela du bloc de données:

> h_no <- data$h_no

Nous voulons détecter quand h_no ne monte pas, ce que nous pouvons faire en déterminant quand la différence entre les éléments successifs est négative ou nulle. R fournit la fonction diff qui nous donne le vecteur des différences:

> d.h_no <- diff(h_no)
> d.h_no
 [1]  1  1  1 -3  1  1  1  1  1  1 -6  1  1  1

Une fois que nous avons cela, il est simple de trouver ceux qui ne sont pas positifs:

> nonpos <- d.h_no <= 0
> nonpos
 [1] FALSE FALSE FALSE  TRUE FALSE FALSE FALSE FALSE FALSE FALSE  TRUE FALSE
[13] FALSE FALSE

Dans R, TRUE et FALSE sont fondamentalement identiques à 1 et 0; ainsi, si nous obtenons la somme cumulative de nonpos, elle augmentera de 1 dans (presque) les emplacements appropriés. La fonction cumsum (qui est fondamentalement le contraire de diff) peut le faire.

> cumsum(nonpos)
 [1] 0 0 0 1 1 1 1 1 1 1 2 2 2 2

Mais, il y a deux problèmes: les chiffres sont un trop petit; et, il nous manque le premier élément (il devrait y en avoir quatre dans la première classe).

Le premier problème est simplement résolu: 1+cumsum(nonpos). Et le second nécessite simplement l’ajout d’un 1 à l’avant du vecteur, car le premier élément est toujours dans la classe 1:

 > classes <- c(1, 1 + cumsum(nonpos))
 > classes
  [1] 1 1 1 1 2 2 2 2 2 2 2 3 3 3 3

Maintenant, nous pouvons le rattacher à notre cadre de données avec cbind (en utilisant la syntaxe class=, nous pouvons donner à la colonne l'en-tête class):

 > data_w_classes <- cbind(data, class=classes)

Et data_w_classes contient maintenant le résultat.

Résultat final

Nous pouvons compresser les lignes ensemble et envelopper le tout dans une fonction facilitant son utilisation:

classify <- function(data) {
   cbind(data, class=c(1, 1 + cumsum(diff(data$h_no) <= 0)))
}

Ou, puisqu'il est logique que class soit un facteur:

classify <- function(data) {
   cbind(data, class=factor(c(1, 1 + cumsum(diff(data$h_no) <= 0))))
}

Vous utilisez soit une fonction comme:

> classified <- classify(data) # doesn't overwrite data
> data <- classify(data) # data now has the "class" column

(Cette méthode de résolution de ce problème est bonne car elle évite les itérations explicites, ce qui est généralement recommandé pour R, et évite de générer beaucoup de vecteurs intermédiaires et de listes etc. Et c'est aussi très bien comment on peut l'écrire sur une ligne :))

7
huon

En plus de la réponse de Roman, une telle chose pourrait être encore plus simple. Notez que je ne l’ai pas testé car je n’ai pas accès à R pour le moment.

# Note that I use a global variable here
# normally not advisable, but I liked the
# use here to make the code shorter
index <<- 0
new_column = sapply(df$h_no, function(x) {
  if(x == 1) index = index + 1
  return(index)
})

La fonction parcourt les valeurs dans n_ho et renvoie toujours la catégorie à laquelle la valeur actuelle appartient. Si une valeur de 1 est détectée, nous augmentons la variable globale index et continuons.

2
Paul Hiemstra
Data.frame[,'h_new_column'] <- as.integer(Data.frame[,'h_no'], breaks=c(1, 4, 7))
1
user2759975

Approche basée sur le nombre de groupes (x dans mapply) et sa longueur (y dans mapply)

mytb<-read.table(text="h_no  h_freq  h_freqsq group
1     0.09091 0.008264628 1
2     0.00000 0.000000000 1
3     0.04545 0.002065702 1
4     0.00000 0.000000000 1  
1     0.13636 0.018594050 2
2     0.00000 0.000000000 2
3     0.00000 0.000000000 2
4     0.04545 0.002065702 2
5     0.31818 0.101238512 2
6     0.00000 0.000000000 2
7     0.50000 0.250000000 2 
1     0.13636 0.018594050 3 
2     0.09091 0.008264628 3
3     0.40909 0.167354628 3
4     0.04545 0.002065702 3", header=T, stringsAsFactors=F)
mytb$group<-NULL

positionsof1s<-grep(1,mytb$h_no)

mytb$newgroup<-unlist(mapply(function(x,y) 
  rep(x,y),                      # repeat x number y times
  x= 1:length(positionsof1s),    # x is 1 to number of nth group = g1:g3
  y= c( diff(positionsof1s),     # y is number of repeats of groups g1 to penultimate (g2) = 4, 7
        nrow(mytb)-              # this line and the following gives number of repeat for last group (g3)
          (positionsof1s[length(positionsof1s )]-1 )  # number of rows - position of penultimate group (g2) 
      ) ) )
mytb
0
Ferroao

vous pouvez d'abord ajouter une colonne vide à votre data.frame puis spécifier vos conditions pour la nouvelle colonne,

agtoexcel2$NONloan <- NA
agtoexcel2$NONloan[agtoexcel2$haveloan==2 & agtoexcel2$ifoloan==2 & agtoexcel2$both==0 ] <- 1
agtoexcel2$NONloan[agtoexcel2$haveloan==1 | agtoexcel2$ifoloan==1 | agtoexcel2$both==1 ] <- 0 
0
Seyma Kalay

Selon moi, utiliser "cbind" est le moyen le plus simple d’ajouter une colonne à une trame de données en R. Ci-dessous un exemple:

    myDf = data.frame(index=seq(1,10,1), Val=seq(1,10,1))
    newCol= seq(2,20,2)
    myDf = cbind(myDf,newCol)
0
Emanuele Catania