data.frame列を因子から文字に変換する


352

データフレームがあります。彼を呼んでみましょうbob

> head(bob)
                 phenotype                         exclusion
GSM399350 3- 4- 8- 25- 44+ 11b- 11c- 19- NK1.1- Gr1- TER119-
GSM399351 3- 4- 8- 25- 44+ 11b- 11c- 19- NK1.1- Gr1- TER119-
GSM399352 3- 4- 8- 25- 44+ 11b- 11c- 19- NK1.1- Gr1- TER119-
GSM399353 3- 4- 8- 25+ 44+ 11b- 11c- 19- NK1.1- Gr1- TER119-
GSM399354 3- 4- 8- 25+ 44+ 11b- 11c- 19- NK1.1- Gr1- TER119-
GSM399355 3- 4- 8- 25+ 44+ 11b- 11c- 19- NK1.1- Gr1- TER119-

このデータフレームの行を連結したいと思います(これは別の質問になります)。でも、見てください:

> class(bob$phenotype)
[1] "factor"

Bobの列は因子です。したがって、たとえば:

> as.character(head(bob))
[1] "c(3, 3, 3, 6, 6, 6)"       "c(3, 3, 3, 3, 3, 3)"      
[3] "c(29, 29, 29, 30, 30, 30)"

私はこれを理解し始めませんでしたが、これらは(カラクタクス王の宮廷の)列の要素のレベルの指標であると思いbobますか?必要なものではありません。

不思議なことに、私はbob手で列を通り抜けることができます

bob$phenotype <- as.character(bob$phenotype)

これは正常に動作します。そして、いくつか入力した後、列が因子ではなく文字であるdata.frameを取得できます。だから私の質問は:これをどうやって自動的に行うことができますか?各列を手動で移動することなく、factor列を含むdata.frameを文字列を含むdata.frameに変換するにはどうすればよいですか?

おまけの質問:手動によるアプローチが機能するのはなぜですか?


3
質問を再現可能にする場合は、の構造を含めてくださいbob
jangorecki

回答:


362

マットとダークをフォローします。グローバルオプションを変更せずに既存のデータフレームを再作成する場合は、applyステートメントを使用して再作成できます。

bob <- data.frame(lapply(bob, as.character), stringsAsFactors=FALSE)

これにより、すべての変数がクラス「文字」に変換されます。因子のみを変換する場合は、以下のMarekのソリューションを参照してください。

@hadleyが指摘するように、以下はより簡潔です。

bob[] <- lapply(bob, as.character)

どちらの場合も、lapplyリストを出力します。ただし、Rの魔法の特性により[]、2番目のケースでを使用すると、bobオブジェクトのdata.frameクラスが保持されるためas.data.frame、引数を使用してdata.frameに戻す必要がなくなりますstringsAsFactors = FALSE


27
シェーン、それはまた数値列を性格に変えるでしょう。
Dirk Eddelbuettel、

@Dirk:それは本当です、それがここで問題であるかどうかは明らかではありません。明らかに、前もって正しく作成することが最善の解決策です。データフレーム全体でデータ型を自動的に変換するのは簡単ではないと思います。1つのオプションは、上記を使用することですが、次にtype.convertすべてをにキャストした後に使用してからcharacter、再度キャストfactorscharacter直します。
シェーン

これは行名を破棄するようです。
piccolbo 2013

2
@piccolboはbob[] <- 例で使用しましたかbob <- ?最初はdata.frameを保持します。2番目は、data.frameをリストに変更し、行名をドロップします。回答を更新します
David LeBauer、2014

6
無名関数を使用して因子列のみを文字に変換するバリアント: iris[] <- lapply(iris, function(x) if (is.factor(x)) as.character(x) else {x})
Stefan F

313

因子のみを置き換えるには:

i <- sapply(bob, is.factor)
bob[i] <- lapply(bob[i], as.character)

バージョン0.5.0のmutate_ifパッケージdplyr で、新しい関数が導入されました

library(dplyr)
bob %>% mutate_if(is.factor, as.character) -> bob

RStudioからのパッケージpurrrは別の選択肢を提供します:

library(purrr)
library(dplyr)
bob %>% map_if(is.factor, as.character) %>% as_tibble -> bob

残念ながら私のために働いていません。理由がわからない。おそらく私はcolnamesを持っているからでしょうか?
Autumnsault 14

@mohawkjohn問題ではないはずです。エラーまたは予期しない結果が発生しましたか?
Marek 14

2
注:このpurrr行はdata.frame!ではなくリストを返します。
RoyalTS

これiは、のベクターであるがすでにある場合にも機能しますcolnames()
口頭発表

39

グローバルオプション

stringsAsFactors:data.frameおよびread.tableの引数のデフォルト設定。

FALSE起動ファイルで設定したいものかもしれません(例:〜/ .Rprofile)。ご覧くださいhelp(options)


5
この問題は、その.Rprofileファイルが存在しない環境でコードを実行すると、バグが発生することです。
2015年

4
.Rprofileに設定があるのではなく、スクリプトの最初に呼び出す傾向があります。
gregmacfarlane

22

因子がどのように格納されるかを理解している場合は、適用ベースの関数を使用してこれを行うことを回避できます。これは、適用ソリューションが適切に機能しないことを意味するものではありません。

因子は、「レベル」のリストに関連付けられた数値インデックスとして構成されます。これは、因子を数値に変換すると確認できます。そう:

> fact <- as.factor(c("a","b","a","d")
> fact
[1] a b a d
Levels: a b d

> as.numeric(fact)
[1] 1 2 1 3

最後の行に返される数値は、因子のレベルに対応しています。

> levels(fact)
[1] "a" "b" "d"

levels()文字の配列を返すことに注意してください。この事実を使用して、次のように因子を文字列または数値に簡単かつコンパクトに変換できます。

> fact_character <- levels(fact)[as.numeric(fact)]
> fact_character
[1] "a" "b" "a" "d"

式をでラップした場合、これは数値でも機能しますas.numeric()

> num_fact <- factor(c(1,2,3,6,5,4))
> num_fact
[1] 1 2 3 6 5 4
Levels: 1 2 3 4 5 6
> num_num <- as.numeric(levels(num_fact)[as.numeric(num_fact)])
> num_num
[1] 1 2 3 6 5 4

この回答は問題に対処していません。これは、データフレームのすべての因子列を文字に変換する方法です。 as.character(f)、読みやすさと効率の両方が優れていlevels(f)[as.numeric(f)]ます。賢くなりたい場合は、levels(f)[f]代わりに使用できます。数値を使用して因子を変換する場合as.numeric(levels(f))[f]、たとえばからメリットが得られますas.numeric(as.character(f))が、これはレベルを数値に変換してからサブセット化するだけでよいためです。as.character(f)そのままで結構です。
De Novo

20

すべての因子ベクトルが文字ベクトルに変換される新しいデータフレームbobcが必要な場合は、次のことを試してください。bobf

bobc <- rapply(bobf, as.character, classes="factor", how="replace")

次にそれを元に戻す場合は、どの列が因子であるかを示す論理ベクトルを作成し、それを使用して因子を選択的に適用できます。

f <- sapply(bobf, class) == "factor"
bobc[,f] <- lapply(bobc[,f], factor)

2
+1は、必要なことだけを行う(つまり、data.frame全体を文字に変換しない)ために使用します。このソリューションは、タイプが混在するdata.frameに対して堅牢です。
Joshua Ulrich 2013

3
この例は、stat.ethz.ch/ R-manual / R-devel / library / base / html / rapply.htmlのように、rapplyの「例」セクションにあるはずです。誰もがそうであることを要求する方法を知っていますか?
mpettis 2013

最終的にデータフレームにしたい場合は、data.frame呼び出しでラップラップを単純にラップします(stringsAsFactorsをFALSE引数に設定して使用)
Taylored Web Sites

13

私は通常、この機能をすべてのプロジェクトの一部にします。早くて簡単。

unfactorize <- function(df){
  for(i in which(sapply(df, class) == "factor")) df[[i]] = as.character(df[[i]])
  return(df)
}

8

別の方法は、applyを使用して変換することです

bob2 <- apply(bob,2,as.character)

そして、より良いもの(前のものはクラス 'matrix'のものです)

bob2 <- as.data.frame(as.matrix(bob),stringsAsFactors=F)

シェーンさんのコメント@次のとおりです。data.frameを得るために、やるas.data.frame(lapply(...
aL3xa

7

更新:機能しない例を次に示します。そうだと思ったのですが、stringsAsFactorsオプションは文字列でのみ機能すると思います。要因はそのままです。

これを試して:

bob2 <- data.frame(bob, stringsAsFactors = FALSE)

一般的に言って、文字であるはずの要素に問題があるときはいつでも、stringsAsFactorsどこかに役立つ設定があります(グローバル設定を含む)。


1
これが機能するのは、作成時に設定した場合ですbob(ただし、事後ではありません)。
シェーン

正しい。これだけでは問題が解決しないことを明確にしたかったのですが、問題が解決されたことに感謝します。
マットパーカー、

7

またはあなたが試すことができますtransform

newbob <- transform(bob, phenotype = as.character(phenotype))

キャラクターに変換したいすべての要素を必ず入れてください。

または、次のようなことをして、すべての害虫を一撃で殺すことができます。

newbob_char <- as.data.frame(lapply(bob[sapply(bob, is.factor)], as.character), stringsAsFactors = FALSE)
newbob_rest <- bob[!(sapply(bob, is.factor))]
newbob <- cbind(newbob_char, newbob_rest)

このようなコードでデータを押し出すの良い考えではありません。そのsapply部分を個別に行うことができます(実際、そのようにするのははるかに簡単です)が、ポイントはわかります...コードをチェックしていないため、私は家にいないので、うまくいくことを願っています!=)

ただし、このアプローチには欠点があります...後で列を再編成する必要transformがありますが、好きなことはできますが、「歩行者スタイルのコード作成」を犠牲にして ...

だから…=)


6

データフレームの最初に、stringsAsFactors = FALSE誤解をすべて無視することを含めます。


4

data.tabledata.frameの操作にパッケージを使用する場合、問題はありません。

library(data.table)
dt = data.table(col1 = c("a","b","c"), col2 = 1:3)
sapply(dt, class)
#       col1        col2 
#"character"   "integer" 

データセットにすでに因子列があり、それらを文字に変換したい場合は、次の操作を実行できます。

library(data.table)
dt = data.table(col1 = factor(c("a","b","c")), col2 = 1:3)
sapply(dt, class)
#     col1      col2 
# "factor" "integer" 
upd.cols = sapply(dt, is.factor)
dt[, names(dt)[upd.cols] := lapply(.SD, as.character), .SDcols = upd.cols]
sapply(dt, class)
#       col1        col2 
#"character"   "integer" 

DTは、Marekによって提案されたsapply修正を回避します。DFを修正In [<-.data.table(*tmp*, sapply(bob, is.factor), : Coerced 'character' RHS to 'double' to match the column's type. Either change the target column to 'character' first (by creating a new 'character' vector length 1234 (nrows of entire table) and assign that; i.e. 'replace' column), or coerce RHS to 'double' (e.g. 1L, NA_[real|integer]_, as.*, etc) to make your intent clear and for speed. Or, set the column type correctly up front when you create the table and stick to it, please.してDTを再作成する方が簡単です。
Matt Chambers

2

これは私のために働きます-私は最終的にワンライナーを考えました

df <- as.data.frame(lapply(df,function (y) if(class(y)=="factor" ) as.character(y) else y),stringsAsFactors=F)


2

たぶん新しいオプションですか?

library("tidyverse")

bob <- bob %>% group_by_if(is.factor, as.character)

1

あなたはパイプと互換性のある読みやすい構文を与えるに使用する必要converthablarありますtidyverse

library(dplyr)
library(hablar)

df <- tibble(a = factor(c(1, 2, 3, 4)),
             b = factor(c(5, 6, 7, 8)))

df %>% convert(chr(a:b))

それはあなたに与える:

  a     b    
  <chr> <chr>
1 1     5    
2 2     6    
3 3     7    
4 4     8   

1

dplyr-packageロードされた使用

bob=bob%>%mutate_at("phenotype", as.character)

phenotype特に-column のみを変更する場合。


0

これはすべてを文字に変換し、次に数値を数値に変換します:

makenumcols<-function(df){
  df<-as.data.frame(df)
  df[] <- lapply(df, as.character)
  cond <- apply(df, 2, function(x) {
    x <- x[!is.na(x)]
    all(suppressWarnings(!is.na(as.numeric(x))))
  })
  numeric_cols <- names(df)[cond]
  df[,numeric_cols] <- sapply(df[,numeric_cols], as.numeric)
  return(df)
}

適応元:Excelシートの列タイプを自動的に取得

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.