ある日本当に驚いたR-落とし穴はありますか? これらを共有することで、私たち全員が利益を得られると思います。
ここに私のものがあります: リストの索引付けでは、my.list[[1]]
is notmy.list[1]
です。R の初期の頃にこれを学びました。
[Hadleyはコメントでこれを指摘しました。]
反復のインデックスとしてシーケンスを使用する場合は、 のseq_along()
ようなものではなく関数を使用することをお勧めします1:length(x)
。
ここでベクトルを作成すると、どちらのアプローチも同じものを返します。
> x <- 1:10
> 1:length(x)
[1] 1 2 3 4 5 6 7 8 9 10
> seq_along(x)
[1] 1 2 3 4 5 6 7 8 9 10
ベクトルを作成しますNULL
。
> x <- NULL
> seq_along(x) # returns an empty integer; good behavior
integer(0)
> 1:length(x) # wraps around and returns a sequence; this is bad
[1] 1 0
これにより、ループ内で混乱が生じる可能性があります。
> for(i in 1:length(x)) print(i)
[1] 1
[1] 0
> for(i in seq_along(x)) print(i)
>
データをロードするときの因子の自動作成。データフレーム内の列を無意識のうちに文字として扱います。これは、値をレベルではない値に変更しようとするようなことをするまではうまく機能します。これにより警告が生成されますが、データフレームにはNAが含まれたままになります...
Rスクリプトで予期しない問題が発生した場合は、要因が原因ではないことを確認してください。
データフレーム内の行を削除すると、一意でない名前の行が追加され、エラーが発生します。
> a<-data.frame(c(1,2,3,4),c(4,3,2,1))
> a<-a[-3,]
> a
c.1..2..3..4. c.4..3..2..1.
1 1 4
2 2 3
4 4 1
> a[4,1]<-1
> a
Error in data.frame(c.1..2..3..4. = c("1", "2", "4", "1"), c.4..3..2..1. = c(" 4", :
duplicate row.names: 4
したがって、ここで何が起こっているかは次のとおりです。
4 行の data.frame が作成されるため、行名は c(1,2,3,4) になります。
3 番目の行が削除されるため、行名は c(1,2,4) になります。
4 番目の行が追加され、R は行名をインデックス、つまり 4 に自動的に設定するため、行名は c(1,2,4,4) になります。行名は一意でなければならないため、これは不正です。このタイプの動作が R で許可される理由がわかりません。R は一意の行名を提供する必要があるように思えます。
行列を 1 次元にサブセット化する際に drop=FALSE 引数を忘れたため、オブジェクト クラスも削除されます。
R> X <- matrix(1:4,2)
R> X
[,1] [,2]
[1,] 1 3
[2,] 2 4
R> class(X)
[1] "matrix"
R> X[,1]
[1] 1 2
R> class(X[,1])
[1] "integer"
R> X[,1, drop=FALSE]
[,1]
[1,] 1
[2,] 2
R> class(X[,1, drop=FALSE])
[1] "matrix"
R>
まず、バイナリ システムで数値を表す基本的な問題を理解していると言わせてください。それにもかかわらず、私が簡単に改善できると思う 1 つの問題は、10 進数値が R の典型的な表示範囲を超えている場合の数値の表示です。
x <- 10.2 * 100
x
1020
as.integer(x)
1019
実際に整数として表現できる場合、結果が整数として表現されてもかまいません。たとえば、値が実際に 1020 だった場合、x にそれを出力しても問題ありません。しかし、この場合、 x を出力するときに 1020.0 のような単純なものを使用すると、値が整数ではなく、1 として表現できないことがより明確になります。R は、表示されない非常に小さな小数部分がある場合に、何らかの表示をデフォルトにする必要があります。
NA
、、NaN
およびの組み合わせを考慮しなければならないのは面倒な場合がありInf
ます。それらは異なる動作をし、1つのテストが他のテストで必ずしも機能するとは限りません。
> x <- c(NA,NaN,Inf)
> is.na(x)
[1] TRUE TRUE FALSE
> is.nan(x)
[1] FALSE TRUE FALSE
> is.infinite(x)
[1] FALSE FALSE TRUE
ただし、これらのトラブルメーカーのいずれかをテストする最も安全な方法は次のとおりです。
> is.finite(x)
[1] FALSE FALSE FALSE
NA
! があるときに何が起こるかを常にテストします。
(多くのつらい経験を経て)私が常に注意を払う必要があることの 1 つは、NA
価値観です。R 関数は使いやすいですが、データに関する問題をプログラミングで解決する方法はありません。
たとえば、 を使用した正味のベクトル演算NA
は と等しくなりNA
ます。これは一見「驚くべき」ことです。
> x <- c(1,1,2,NA)
> 1 + NA
[1] NA
> sum(x)
[1] NA
> mean(x)
[1] NA
これは、他の高レベル関数に外挿されます。
つまり、欠損値は、多くの場合、デフォルトで測定値と同じくらい重要です。多くの関数にはna.rm=TRUE/FALSE
デフォルトがあります。これらのデフォルト設定をどのように解釈するかを決めるのに時間を費やす価値があります。
編集1:マレクは素晴らしい点を挙げています. NA
値は、インデックスで紛らわしい動作を引き起こす可能性もあります。例えば:
> TRUE && NA
[1] NA
> FALSE && NA
[1] FALSE
> TRUE || NA
[1] TRUE
> FALSE || NA
[1] NA
これは、(if ステートメントの) 条件式を作成しようとしている場合にも当てはまります。
> any(c(TRUE, NA))
[1] TRUE
> any(c(FALSE, NA))
[1] NA
> all(c(TRUE, NA))
[1] NA
これらの NA 値が最終的にベクトル インデックスになると、多くの予期しないことが続く可能性があります。これは、欠損値に注意する必要があることを意味するため、R ではすべて適切な動作です。しかし、最初は大きな頭痛を引き起こす可能性があります。
関数はround
常に偶数に丸められます。
> round(3.5)
[1] 4
> round(4.5)
[1] 4
それを忘れるとstrptime()
、友達はいつも 9 のPOSIXt POSIXlt
場所に戻ってきます。length()
変換すると次のようになりPOSIXct
ます。
R> length(strptime("2009-10-07 20:21:22", "%Y-%m-%d %H:%M:%S"))
[1] 9
R> length(as.POSIXct(strptime("2009-10-07 20:21:22", "%Y-%m-%d %H:%M:%S")))
[1] 1
R>
整数の数学は double とは微妙に異なります (また、複素数も奇妙である場合があります)。
UPDATE彼らはR 2.15でいくつかのことを修正しました
1^NA # 1
1L^NA # NA
(1+0i)^NA # NA
0L %/% 0L # 0L (NA from R 2.15)
0 %/% 0 # NaN
4L %/% 0L # 0L (NA from R 2.15)
4 %/% 0 # Inf
誰もこれについて言及していないことに驚いていますが、
T
&F
は上書きできますが、TRUE
&FALSE
はできません。
例:
x <- sample(c(0,1,NA), 100, T)
T <- 0:10
mean(x, na.rm=T)
# Warning in if (na.rm) x <- x[!is.na(x)] :
# the condition has length > 1 and only the first element will be used
# Calls: mean -> mean.default
# [1] NA
plot(rnorm(7), axes=T)
# Warning in if (axes) { :
# the condition has length > 1 and only the first element will be used
# Calls: plot -> plot.default
# Warning in if (frame.plot) localBox(...) :
# the condition has length > 1 and only the first element will be used
# Calls: plot -> plot.default
[編集]ctrf+F
私をだまします。シェーンは彼のコメントでこれについて言及しています。
データの読み取りは、あなたが思っているよりも問題になる可能性があります。今日、 read.csv()を使用すると、.csv ファイルの行が空白の場合、read.csv()が自動的にスキップすることがわかりました。これはほとんどのアプリケーションにとって理にかなっていますが、(たとえば) 数千のファイルから 27 行目からデータを自動的に抽出する場合、その前の行の一部が空白である場合とそうでない場合があります。違う。
今使ってる
data1 <- read.table(file_name, blank.lines.skip = F, sep = ",")
データをインポートするときは、実際に行っていると思っていることを何度も何度も行っていることを確認してください...
関数のトリッキーな動作all.equal()
。
私の継続的なエラーの 1 つは、一連の浮動小数点数を比較することです。次のような CSV があります。
... mu, tau, ...
... 0.5, 1.7, ...
ファイルを読み取ってデータをサブセット化しようとすると、うまくいくこともあれば失敗することもあります - もちろん、浮動小数点トラップの穴に何度も何度も陥るからです。最初は、データには整数値のみが含まれていましたが、後で常に実数に変換されます。これはご存知のとおりです。比較は演算子でall.equal()
はなく関数で行う必要があります==
が、もちろん、最初に書いたコードは後者のアプローチを使用していました。
ええ、クールですが、同じ数をall.equal()
返しますTRUE
が、失敗した場合はテキストのエラー メッセージが表示されます。
> all.equal(1,1)
[1] TRUE
> all.equal(1:10, 1:5)
[1] "Numeric: lengths (10, 5) differ"
> all.equal(1:10, c(1:5,1:5))
[1] "Mean relative difference: 0.625"
解決策は関数を使用していisTRUE()
ます:
if (!isTRUE(all.equal(x, y, tolerance=doubleErrorRate))) {
...
}
何度all.equals()
説明を読んだことか…
これは非常に痛かったので、バグレポートにコメントを追加するのに何時間も費やしました。望みは叶いませんでしたが、少なくとも次のバージョンの R ではエラーが発生するでしょう。
R> nchar(factor(letters))
[1] 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
更新: R 3.2.0 以降 (おそらくそれ以前)、この例ではエラー メッセージが生成されるようになりました。以下のコメントで述べたように、因子はベクトルではなく、nchar() にはベクトルが必要です。
R> nchar(factor(letters))
Error in nchar(factor(letters)) : 'nchar()' requires a character vector
R> is.vector(factor(letters))
[1] FALSE
空の括弧を含めるのを忘れて、誤って関数のソース コードをリストする: 例: "ls" 対 "ls()"
true と false は、Matlab、C++、Java、Python のように、事前定義された定数としてカットしません。TRUE と FALSE を使用する必要があります
非表示の戻り値: たとえば、".packages()" は何も返しませんが、"(.packages())" はパッケージ ベース名の文字ベクトルを返します。
たとえば、数値3.14は数値定数ですが、式+3.14および-3.14は関数+
および-
:の呼び出しです。
> class(quote(3.14))
[1] "numeric"
> class(quote(+3.14))
[1] "call"
> class(quote(-3.14))
[1] "call"
JohnChambersの著書「SoftwareforDataAnalysis -ProgrammingwithR 」のセクション13.2を参照してください。
Partial matching in the $
operator:
This applies to lists, but also on data.frame
df1 <- data.frame(foo=1:10, foobar=10:1)
df2 <- data.frame(foobar=10:1)
df1$foo # Correctly gets the foo column
df2$foo # Expect NULL, but this returns the foobar column!!!
# So, should use double bracket instead:
df1[["foo"]]
df2[["foo"]]
The [[
operator also has an exact
flag, but it is thankfully TRUE
by default.
Partial matching also affects attr
:
x1 <- structure(1, foo=1:10, foobar=10:1)
x2 <- structure(2, foobar=10:1)
attr(x1, "foo") # Correctly gets the foo attribute
attr(x2, "foo") # Expect NULL, but this returns the foobar attribute!!!
# So, should use exact=TRUE
attr(x1, "foo", exact=TRUE)
attr(x2, "foo", exact=TRUE)
長さゼロのベクトルにはいくつかの癖があります。
R> kk=vector(mode="numeric",length=0)
R> kk
numeric(0)
R> sum(kk)
[1] 0
R> var(kk)
[1] NA
インデックスとして使用されるベクトルの自動繰り返し ( "recycling" ):
R> all.numbers <- c(1:5)
R> all.numbers
[1] 1 2 3 4 5
R> good.idxs <- c(T,F,T)
R> #note unfortunate length mismatch
R> good.numbers <- all.numbers[good.idxs]
R> good.numbers
[1] 1 3 4
R> #wtf?
R> #why would you repeat the vector used as an index
R> #without even a warning?
リストを操作すると、直感的でないことがいくつかあります。
もちろん、 と の違いには[
慣れ[[
が必要です。リストの場合、 は[
(場合によっては 1 つの) 要素のリストを返しますが、[[
はリスト内の要素を返します。
リストの作成:
# When you're used to this:
x <- numeric(5) # A vector of length 5 with zeroes
# ... this might surprise you
x <- list(5) # A list with a SINGLE element: the value 5
# This is what you have to do instead:
x <- vector('list', 5) # A vector of length 5 with NULLS
では、NULL をリストに挿入する方法は?
x <- list("foo", 1:3, letters, LETTERS) # A sample list
x[[2]] <- 1:5 # Put 1:5 in the second element
# The obvious way doesn't work:
x[[2]] <- NULL # This DELETES the second element!
# This doesn't work either:
x[2] <- NULL # This DELETES the second element!
# The solution is NOT very intuitive:
x[2] <- list(NULL) # Put NULL in the second element
# Btw, now that we think we know how to delete an element:
x <- 1:10
x[[2]] <- NULL # Nope, gives an ERROR!
x <- x[-2] # This is the only way for atomic vectors (works for lists too)
最後に、ネストされたリストを介したインデックス作成などの高度なもの:
x <- list(a=1:3, b=list(c=42, d=13, e="HELLO"), f='bar')
x[[c(2,3)]] # HELLO (first selects second element and then it's third element)
x[c(2,3)] # The second and third elements (b and f)
R での大きな混乱の 1 つは、[i, drop = TRUE]
因子レベルを下げることですが、そうで[i, j, drop = TRUE]
はありません!
> df = data.frame(a = c("europe", "asia", "oceania"), b = c(1, 2, 3))
> df$a[1:2, drop = TRUE]
[1] europe asia
Levels: asia europe <---- drops factor levels, works fine
> df[1:2,, drop = TRUE]$a
[1] europe asia
Levels: asia europe oceania <---- does not drops factor levels!
詳細については、以下を参照してください: drop = TRUE は、ベクトルではドロップしますが、data.frame ではファクター レベルをドロップしません。
私にとっては、を使用してdata.frameをテキストファイルにエクスポートし、後でwrite.csv
それをインポートするときに、次のようにまったく同じdata.frameを取得するために引数を追加する必要があるという直感に反する方法です。
write.csv(m, file = 'm.csv')
read.csv('m.csv', row.names = 1) # Note the row.names argument
私もこの質問をSOに投稿し、@BenBolkerによってこのQへの回答として提案されました。
コンパイルされた言語と Matlab から来て、関数型言語の関数の基本的な側面について時々混乱することがあります。関数は使用する前に定義する必要があります。R インタープリターによって解析されるだけでは十分ではありません。ネストされた関数を使用する場合、これはほとんど頭をもたげます。
Matlab では、次のことができます。
function f1()
v1 = 1;
v2 = f2();
fprintf('2 == %d\n', v2);
function r1 = f2()
r1 = v1 + 1 % nested function scope
end
end
R で同じことをしようとすると、ネストされた関数を最初に配置する必要があります。そうしないと、エラーが発生します。関数を定義したからといって、変数に割り当てられるまで名前空間にはありません! 一方、関数はまだ定義されていない変数を参照できます。
f1 <- function() {
f2 <- function() {
v1 + 1
}
v1 <- 1
v2 = f2()
print(sprintf("2 == %d", v2))
}
今日の私のもの: qnorm() は確率を取り、pnorm() は分位数を取ります。
関数のapply
セットは、行列に対して機能するだけでなく、多次元配列までスケールアップします。私の研究では、大気の温度などのデータセットをよく持っています。これは、次元 の多次元配列に格納され、今後はx,y,level,time
と呼ばれmulti_dim_array
ます。モックアップの例は次のとおりです。
multi_dim_array = array(runif(96 * 48 * 6 * 100, -50, 50),
dim = c(96, 48, 6, 100))
> str(multi_dim_array)
# x y lev time
num [1:96, 1:48, 1:6, 1:100] 42.4 16 32.3 49.5 24.9 ...
1つを使用apply
すると、次のものが簡単に取得できます。
# temporal mean value
> str(apply(multi_dim_array, 4, mean))
num [1:100] -0.0113 -0.0329 -0.3424 -0.3595 -0.0801 ...
# temporal mean value per gridcell (x,y location)
> str(apply(multi_dim_array, c(1,2), mean))
num [1:96, 1:48] -1.506 0.4553 -1.7951 0.0703 0.2915 ...
# temporal mean value per gridcell and level (x,y location, level)
> str(apply(multi_dim_array, c(1,2,3), mean))
num [1:96, 1:48, 1:6] -3.839 -3.672 0.131 -1.024 -2.143 ...
# Spatial mean per level
> str(apply(multi_dim_array, c(3,4), mean))
num [1:6, 1:100] -0.4436 -0.3026 -0.3158 0.0902 0.2438 ...
これにより、margin
議論apply
は直感に反するものではなくなります。最初に、1 と 2 の代わりに "row" と "col" を使用してみませんか。しかし、より多くの次元を持つ配列でも機能するという事実は、margin
このような使用が好まれる理由を明確にします。