Rの一貫性のない形式のデータをクリーニングしますか?


16

統計を行う前に多くのクリーンアップを必要とする厄介な調査データをよく扱います。私はこれをExcelで「手動」で実行していました。Excelの式を使用したり、エントリを1つずつ確認したりすることもありました。これらのタスクの多くをRで実行するスクリプトを作成することで始めました。これは非常に有益でした(実行したことの記録、ミスの可能性の減少、データセットが更新しました)。

しかし、効率的に処理するのが難しいいくつかの種類のデータがまだあります。例えば:

> d <- data.frame(subject = c(1,2,3,4,5,6,7,8,9,10,11),
+   hours.per.day = c("1", "2 hours", "2 hr", "2hr", "3 hrs", "1-2", "15 min", "30 mins", "a few hours", "1 hr 30 min", "1 hr/week"))
> d
   subject hours.per.day
1        1             1
2        2       2 hours
3        3          2 hr
4        4           2hr
5        5         3 hrs
6        6           1-2
7        7        15 min
8        8       30 mins
9        9   a few hours
10      10   1 hr 30 min
11      11     1 hr/week

hours.per.dayは、特定のアクティビティに費やされる1日あたりの平均時間数を意味しますが、私たちが持っているのは、まさに被験者が書いたものです。あいまいな応答をどうするかについていくつかの決定を下しhours.per.day2、次のように整理された変数が必要だとします。

   subject hours.per.day hours.per.day2
1        1             1      1.0000000
2        2       2 hours      2.0000000
3        3          2 hr      2.0000000
4        4           2hr      2.0000000
5        5         3 hrs      3.0000000
6        6           1-2      1.5000000
7        7        15 min      0.2500000
8        8       30 mins      0.5000000
9        9   a few hours      3.0000000
10      10   1 hr 30 min      1.5000000
11      11     1 hr/week      0.1428571

ケースの数が非常に多く(1000など)、被験者が好きなものを自由に書くことができると知っていると仮定すると、これにアプローチする最良の方法は何ですか?

回答:


12

gsub()を使用して、知っている文字列を特定し、その後、おそらく残りを手作業で行います。

test <- c("15min", "15 min", "Maybe a few hours", 
          "4hr", "4hour", "3.5hr", "3-10", "3-10")
new_var <- rep(NA, length(test))

my_sub <- function(regex, new_var, test){
    t2 <- gsub(regex, "\\1", test)
    identified_vars <- which(test != t2)
    new_var[identified_vars] <- as.double(t2[identified_vars])
    return(new_var)    
}

new_var <- my_sub("([0-9]+)[ ]*min", new_var, test)
new_var <- my_sub("([0-9]+)[ ]*(hour|hr)[s]{0,1}", new_var, test)

手動で変更する必要があるものを使用するには、次のようなものをお勧めします。

# Which have we not found
by.hand <- which(is.na(new_var))

# View the unique ones not found
unique(test[by.hand])
# Create a list with the ones
my_interpretation <- list("3-10"= 5, "Maybe a few hours"=3)
for(key_string in names(my_interpretation)){
    new_var[test == key_string] <- unlist(my_interpretation[key_string])
}

これは与える:

> new_var
[1] 15.0 15.0  3.0  4.0  4.0  3.5  5.0  5.0

正規表現は少し注意が必要です。正規表現で何かをするたびに、いくつかの簡単なテストを実行します。Se?regexのマニュアル。基本的な動作は次のとおりです。

> # Test some regex
> grep("[0-9]", "12")
[1] 1
> grep("[0-9]", "12a")
[1] 1
> grep("[0-9]$", "12a")
integer(0)
> grep("^[0-9]$", "12a")
integer(0)
> grep("^[0-9][0-9]", "12a")
[1] 1
> grep("^[0-9]{1,2}", "12a")
[1] 1
> grep("^[0-9]*", "a")
[1] 1
> grep("^[0-9]+", "a")
integer(0)
> grep("^[0-9]+", "12222a")
[1] 1
> grep("^(yes|no)$", "yes")
[1] 1
> grep("^(yes|no)$", "no")
[1] 1
> grep("^(yes|no)$", "(yes|no)")
integer(0)
> # Test some gsub, the \\1 matches default or the found text within the ()
> gsub("^(yes|maybe) and no$", "\\1", "yes and no")
[1] "yes"

答えてくれてありがとう。私は正規表現に精通していないので、それらについて学ぶ必要があります。残りの作業を手作業で行う方法を簡単に説明していただけますか?ちょうど何かやってより良い方法があるnew_var[by.hand] <- c(2, 1, ...)by.handされてTRUE手作業で行われ例のためには?
mark999

@ mark999:いくつかの例を追加し、それらを手作業で行う方法の提案を追加しました。
マックスゴードン

1
正規表現は、OPが持っているようにデータをクリーンアップしたり、ファイル、HTMLなどからデータを抽出したりする、あらゆる種類のデータ操作にとって非常に重要です(適切なHTMLには、XMLデータの抽出に役立つライブラリがありますが、 HTMLの形式が正しくない場合は機能しません。)
ウェイン

6

@Maxの提案は良いものです。数字だけでなく、一般的な時間に関連する単語/略語を認識するアルゴリズムを作成すれば、ほとんどの方法が得られるようです。これは美しいコードではありませんが、機能し、問題が発生した場合は時間をかけて改善できます。

しかし、より堅牢な(そして最初は時間のかかる)アプローチのために、グーグルの「自然言語の時間文字列の解析」を試してください。興味深い発見は、このオープンタイムAPI、優れたPythonモジュール、およびStack Overflowにあるこのような多くの密接なスレッドの1つです。

基本的に、自然言語の解析は一般的な問題であり、R以外の言語でソリューションを探す必要があります。Rを使用してアクセスできる別の言語でツールを構築できます。少なくとも、独自のアルゴリズムの良いアイデアを得ることができます。


4

そのような何かのために、それが十分に長かった場合、正規表現と変換ルールのリストが必要で、新しい値を別の列に持っていくと思います(したがって、生データをリロードせずに常に二重チェックする機会があります) ; すべてのデータが変換されるか、すべてのルールが使い果たされるまで、REはそれほど変換されていないデータに適用されます。どの行がまだ変換されていないかを示す論理値のリストを保持することもおそらく最善です。

もちろん、このようなルールのいくつかは明らかであり、おそらく80〜90%のケースを処理しますが、問題は常にあなたが知らないものが出てくることです(人々は非常に独創的です)。

次に、未確認のルールのリストでまだ変換されていない元の値を一度に1つずつ表示して、正規表現を作成する機会を与えるスクリプトが必要です(たとえば、 )それらのケースを識別し、それに適合するケースの新しい変換を提供します。これは元のリストに追加され、元のベクトルのまだ変換されていない行に適用されてから、提示するケースが残っているかどうかを確認します。

また、ケースをスキップするオプションを用意して(より簡単なケースに進むことができるように)して、非常に難しいケースを最後まで吐き出すこともできます。

最悪の場合、手作業でいくつかを行います。

その後、生成したルールの完全なリストを保持して、データが大きくなったとき、または新しい類似のデータセットが出現したときに再び適用できます。

それがリモートでベストプラクティスに近づいているかどうかはわかりませんが(そこではもっとフォーマルなものが必要だと思います)、大量のそのようなデータを迅速に処理するという点では、何らかの価値があるかもしれません。


答えてくれてありがとう、グレン。とても魅力的ですね。すべてを表示してその出力を見るのではなく、まだ変換されていない値を一度に1つずつ表示することは大きな利点だと思いますか?一度に1つずつ発表するようなことはしたことがありません。
mark999

1
@ mark999、一度に1つのプレゼンテーションには長所と短所の両方があると思います。利点はシンプルです-cat()を使用してあいまいな時間を表示し、scan()を使用してその時間の解釈を記録するのは簡単です。欠点は、1行の正規表現コードでまとめて修正できる多くのエントリの全体像を見逃す可能性があることです。あなたが何を手に入れたいかについて考えているかもしれません:この問題を解決したいだけなら、手でそれをしてください。Rについて詳しく知りたい場合は、ソリューションをコーディングしてみてください。
アッシュ

返信がなくて申し訳ありません。Ashのコメントに広く同意します
Glen_b -Reinstate Monica

4

Rは、いくつか含まれている標準の中、データクリーニングのために使用することができるデータ操作のための機能をベース(パッケージgsubtransform等、等)、ならびに様々なサードパーティ製のパッケージにstringr変形 reshape2、及びplyr。例およびこれらのパッケージとその機能の使用のベストプラクティスは、次の論文に記載されている:http://vita.had.co.nz/papers/tidy-data.pdf

さらに、Rは、データのクリーニングと変換に特化したパッケージをいくつか提供しています。

包括的かつコヒーレントなアプローチクリーニングデータの例および使用を含むRに、editrulesdeducorrectパッケージ、並びにの説明ワークフローフレームワーク Rで洗浄データの)は、私は非常にお勧めしており、以下の論文に提示されている:HTTP ://cran.r-project.org/doc/contrib/de_Jonge+van_der_Loo-Introduction_to_data_cleaning_with_R.pdf

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