Есть ли определенная R-гоча, которую вы действительно удивились однажды? Я думаю, мы все выиграем от совместного использования.
Здесь мой: в индексировании списка my.list[[1]]
не my.list[1]
. Узнал это в ранние дни Р..
Есть ли определенная R-гоча, которую вы действительно удивились однажды? Я думаю, мы все выиграем от совместного использования.
Здесь мой: в индексировании списка my.list[[1]]
не my.list[1]
. Узнал это в ранние дни Р..
Удаление строк в фрейме данных приведет к добавлению не-однозначно названных строк, что приведет к ошибкам:
> 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
Так что происходит здесь:
Создается четырехстрочный data.frame, поэтому имена ростов - c (1,2,3,4)
Третья строка удаляется, поэтому имена ростов - c (1,2,4)
Добавлена четвертая строка, и R автоматически устанавливает имя строки, равную индексу i.e. 4, поэтому имена строк c (1,2,4,4). Это незаконно, потому что имена строк должны быть уникальными. Я не понимаю, почему этот тип поведения должен быть разрешен R. Мне кажется, что R должно предоставить уникальное имя строки.
[Хэдли указал на это в комментарии.]
При использовании последовательности в качестве индекса для итерации лучше использовать функцию 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 script, убедитесь, что факторы не виноваты.
Забыть аргумент 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>
Во-первых, позвольте мне сказать, что я понимаю фундаментальные проблемы представления чисел в двоичной системе. Тем не менее, одна проблема, которая, по моему мнению, может быть легко улучшена, - это представление чисел, когда десятичное значение выходит за пределы R типичной области представления.
x <- 10.2 * 100
x
1020
as.integer(x)
1019
Я не возражаю, если результат представлен как целое, когда он действительно может быть представлен как целое число. Например, если значение действительно было 1020, то печать, которая для x была бы прекрасной. Но что-то столь же простое, как 1020.0 в этом случае при печати x, сделало бы более очевидным, что значение не было целым и не представлялось как единое целое. R должен по умолчанию указывать какую-либо индикацию, когда имеется чрезвычайно маленький десятичный компонент, который не представлен.
Может быть досадно иметь возможность сочетать NA
, NaN
и Inf
. Они ведут себя по-другому, и тесты для одного не обязательно будут работать для других:
> 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
!
Одна вещь, на которую мне всегда нужно уделять пристальное внимание (после многих болезненных переживаний), - это значения NA
. Функции R просты в использовании, но никакие способы программирования не будут устранять проблемы с вашими данными.
Например, любая операция net vector с NA
равна NA
. Это "удивительно" на первый взгляд:
> x <- c(1,1,2,NA)
> 1 + NA
[1] NA
> sum(x)
[1] NA
> mean(x)
[1] NA
Это экстраполируется на другие функции более высокого уровня.
Другими словами, отсутствующие значения часто имеют такое же значение, как измеренные значения по умолчанию. У многих функций есть na.rm=TRUE/FALSE
defaults; стоит потратить некоторое время на решение, как интерпретировать эти настройки по умолчанию.
Изменить 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, потому что это означает, что вы должны быть осторожны с отсутствующими значениями. Но это может вызвать серьезные головные боли в начале.
Забыв, что strptime()
и друзья возвращаются 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>
Функция round
всегда округляется до четного числа.
> round(3.5)
[1] 4
> round(4.5)
[1] 4
Математика для целых чисел тонко отличается от двойников (а иногда и сложная тоже странная)
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()
.
Одна из моих постоянных ошибок - это сравнение числа чисел с плавающей запятой. У меня есть 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, С++, 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"
См. раздел 13.2 в книге Джона Чамберса Программное обеспечение для анализа данных - Программирование с помощью R
Частичное совпадение в операторе $
:
Это относится к спискам, но также и к 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"]]
Оператор [[
также имеет флаг exact
, но, к счастью, TRUE
по умолчанию.
Частичное сопоставление также влияет на 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
Работая со списками, есть несколько неинтуитивных вещей:
Конечно, разница между [
и [[
немного приближается. Для списков [
возвращает список (потенциально 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 third element)
x[c(2,3)] # The second and third elements (b and f)
Автоматическое повторение векторов ("рециркуляция" ), используемых в качестве показателей:
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?
Одна из большой путаницы в R заключается в том, что [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, в то время как в векторе он делает
Исходя из компилируемого языка и 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() принимает Quantiles.
Для меня это счетчик интуитивно понятный способ, в котором при экспорте 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 и был предложен как ответ на этот вопрос Q @BenBolker.
Набор функций 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 ...
Используя 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
менее интуитивным. Сначала я хочу, почему бы не использовать "row" и "col" вместо 1 и 2. Но тот факт, что он также работает для массива с большим количеством измерений, дает понять, почему использование margin
, как это предпочтительнее.