web-dev-qa-db-fra.com

R data.table change R names

J'ai créé un petit data.table DT = data.table(a=1:2, a=1:2).

Si j'utilise names(DT) <- c("b","b")

Je reçois un avertissement

In `names<-.data.table`(`*tmp*`, value = c("b", "b")) :
  The names(x)<-value syntax copies the whole table. This is due to <- in R itself. Please change to setnames(x,old,new) which does not copy and is faster. See help('setnames'). You can safely ignore this warning if it is inconvenient to change right now. Setting options(warn=2) turns this warning into an error, so you can then use traceback() to find and change your names<- calls.

Mais si j'utilise setnames(DT, names(DT), c("b","b"), alors j'obtiens une erreur

Error in setnames(DT, names(DT), c("b", "b")) : 
  Some duplicates exist in 'old': a

Si le même exemple fait avec data.frame que DT = data.frame(a=1:2, a=1:2) et utilise names(DT) <- c("b","b") alors je ne reçois aucune erreur.

24
user2771940

Ne fournissez pas old et new et vous n'aurez pas de problème. Cependant, ce n'est pas le problème. Dans base::data.frame Vous ne pouvez pas avoir de colonnes du même nom donc ...

#  What you actually get...
DT = data.frame(a=1:2, a=1:2); names(DT)
#[1] "a"   "a.1"

Mais il semble que dans data.table Vous pouvez avoir des colonnes du même nom ...

DT = data.table(a=1:2, a=1:2); names(DT)
[1] "a" "a"

Mais setnames génère une erreur, je suppose parce qu'il ne sait pas à quelle colonne a fait référence lorsque les deux colonnes sont appelées a. Vous n'obtenez aucune erreur lorsque vous passez la route data.frame Vers data.table Car vous n'avez pas de noms de colonne dupliqués.

Tout d'abord, je dirais de ne pas créer de colonnes avec le même nom, c'est une très mauvaise chose si vous prévoyez de utilisez votre data.table par programme (mais comme le souligne @MatthewDowle dans les commentaires, c'est un choix de conception pour donner à l'utilisateur une liberté maximale dans data.table).

Si vous devez le faire, utilisez setnames avec juste l'argument old donné, qui sera en fait traité comme les noms new lorsque new n'est pas donné . Si vous passez old names et un vecteur de nouveaux noms, les anciens noms sont trouvés et ceux changés pour le nouveau nom correspondant (donc old et new doivent être de la même longueur lorsque setnames est utilisé avec 3 paramètres). setnames interceptera toute ambiguïté via:

if (any(duplicated(old))) 
           stop("Some duplicates exist in 'old': ", paste(old[duplicated(old)],
                collapse = ","))
if (any(duplicated(names(x)))) 
           stop("'old' is character but there are duplicate column names: ", 
                paste(names(x)[duplicated(names(x))], collapse = ",")) 

Lorsque juste old est fourni setnames réaffectera les noms de old aux colonnes de DT colonne par ligne en utilisant .Call(Csetcharvec, names(x), seq_along(names(x)), old), donc du premier au dernier ...

DT = data.table(a=1:2, a=1:2)
setnames(DT, c("b","b") )
DT
#   b b
#1: 1 1
#2: 2 2

Ajout de Matthew comme demandé. Dans ?setnames Il y a quelques informations:

Ce n'est pas une bonne pratique de programmation, en général, d'utiliser des numéros de colonne plutôt que des noms. C'est pourquoi setkey et setkeyv n'acceptent que les noms de colonnes, et pourquoi old in setnames () est recommandé pour être des noms. Si vous utilisez des numéros de colonne, les bogues (éventuellement silencieux) peuvent plus facilement se glisser dans votre code au fil du temps si des modifications sont apportées ailleurs dans votre code; Par exemple, si vous ajoutez, supprimez ou réorganisez des colonnes dans quelques mois, une clé de jeu par numéro de colonne se référera alors à une autre colonne, renvoyant éventuellement des résultats incorrects sans avertissement. (Un concept similaire existe en SQL, où "select * from ..." est considéré comme un mauvais style de programmation lorsqu'un système robuste et maintenable est requis.) Si vous souhaitez vraiment utiliser des numéros de colonne, c'est possible mais délibérément un peu plus difficile; par exemple, setkeyv (DT, noms de colonnes (DT) [1: 2]).

[Depuis juillet 2017, la note ci-dessus n'apparaît plus dans ?setnames, Mais le problème est abordé en haut de la FAQ, vignette('datatable-faq').]

L'idée de setnames est donc de changer très facilement un nom de colonne, par son nom.

setnames(DT, "oldname", "newname")

Si "oldname" N'est pas un nom de colonne ou s'il y a une ambiguïté sur ce que vous prévoyez (soit dans les données maintenant, soit dans quelques mois après que vos collègues ont changé la base de données source ou un autre code en amont ou ont transmis leurs propres données à votre module) alors data.table l'attrapera pour vous. C'est en fait assez difficile à faire en base aussi facilement et aussi bien que setnames le fait (y compris les contrôles de sécurité).

31
Simon O'Hanlon

setnames peut être utilisé pour changer plusieurs noms de colonne à la fois:

setnames(DT, old = c("oldname1", "oldname2", "oldname3"), new = c("newname1", "newname2", "newname3"))
4
Stephen