Какой самый полезный трюк с R? [закрыто]


88

Чтобы поделиться еще несколькими советами и приемами для R , какая ваша самая полезная функция или прием? Умная векторизация? Ввод / вывод данных? Визуализация и графика? Статистический анализ? Специальные функции? Сама интерактивная среда?

По одному пункту на публикацию, и мы увидим победителя с помощью голосов.

[Править 25 августа 2008 г.]: Итак, через неделю, кажется, что простой str()победил в опросе. Поскольку я сам люблю рекомендовать это, это простой ответ.


8
@Dirk: «вики сообщества» означает «принадлежащий сообществу», а не синоним «вопрос опроса». Не слушайте вики-полицию сообщества.
Джульетта


8
CW снова запугивает. Я посмотрю на вашу мета-SO и подниму вас: meta.stackexchange.com/questions/392/…
ars

13
@ars: это вопрос, на который нет однозначного ответа . Ergo сделай это CW.
dmckee --- котенок экс-модератора

2
@JD Длинный веселый комментарий. к сожалению, он был спрятан за складкой. Я имею в виду, что отвечать на сложные вопросы R на самом деле не выгодно. Так что для меня нормально, если парни, которые задают красивые вопросы, которые делают R на карте, наконец, получат некоторую заслугу. Кроме того, это, безусловно, более полезно для пользователей R, чем ваш любимый вопрос о C-трюке для программистов на C ...
Мэтт Баннерт

Ответы:


64

str() сообщает вам структуру любого объекта.


Python использует dir()- имеет больше смысла.
Хэмиш Грубиджан,

17
Ах, strэто тоже сокращение от stringмногих языков.
Хэмиш Грубиджан,

А почему бы и нет class()? Кажется, выявляет подобную информацию. Почему есть две такие похожие команды?
чч

1
class()- это лишь малая часть str()отображаемой информации
Hadley 06

64

Одна очень полезная функция, которую я часто использую, - это dput (), которая позволяет вам выгружать объект в виде кода R.

# Use the iris data set
R> data(iris)
# dput of a numeric vector
R> dput(iris$Petal.Length)
c(1.4, 1.4, 1.3, 1.5, 1.4, 1.7, 1.4, 1.5, 1.4, 1.5, 1.5, 1.6, 
1.4, 1.1, 1.2, 1.5, 1.3, 1.4, 1.7, 1.5, 1.7, 1.5, 1, 1.7, 1.9, 
1.6, 1.6, 1.5, 1.4, 1.6, 1.6, 1.5, 1.5, 1.4, 1.5, 1.2, 1.3, 1.4, 
1.3, 1.5, 1.3, 1.3, 1.3, 1.6, 1.9, 1.4, 1.6, 1.4, 1.5, 1.4, 4.7, 
4.5, 4.9, 4, 4.6, 4.5, 4.7, 3.3, 4.6, 3.9, 3.5, 4.2, 4, 4.7, 
3.6, 4.4, 4.5, 4.1, 4.5, 3.9, 4.8, 4, 4.9, 4.7, 4.3, 4.4, 4.8, 
5, 4.5, 3.5, 3.8, 3.7, 3.9, 5.1, 4.5, 4.5, 4.7, 4.4, 4.1, 4, 
4.4, 4.6, 4, 3.3, 4.2, 4.2, 4.2, 4.3, 3, 4.1, 6, 5.1, 5.9, 5.6, 
5.8, 6.6, 4.5, 6.3, 5.8, 6.1, 5.1, 5.3, 5.5, 5, 5.1, 5.3, 5.5, 
6.7, 6.9, 5, 5.7, 4.9, 6.7, 4.9, 5.7, 6, 4.8, 4.9, 5.6, 5.8, 
6.1, 6.4, 5.6, 5.1, 5.6, 6.1, 5.6, 5.5, 4.8, 5.4, 5.6, 5.1, 5.1, 
5.9, 5.7, 5.2, 5, 5.2, 5.4, 5.1)
# dput of a factor levels
R> dput(levels(iris$Species))
c("setosa", "versicolor", "virginica")

Может быть очень полезно размещать легко воспроизводимые фрагменты данных, когда вы просите о помощи, или редактировать или изменять порядок уровней фактора.


42

head () и tail (), чтобы получить первую и последнюю части фрейма данных, вектора, матрицы, функции и т. д. Это быстрый способ проверить правильность загрузки, особенно с большими фреймами данных.


38

Одна приятная функция: для чтения данных используются соединения, которые могут быть локальными файлами, удаленными файлами, доступными через http, каналами из других программ и т. Д.

В качестве простого примера рассмотрим этот доступ для N = 10 случайных целых чисел от min = 100 до max = 200 с сайта random.org (который предоставляет истинные случайные числа, основанные на атмосферном шуме, а не на генераторе псевдослучайных чисел):

R> site <- "http://random.org/integers/"         # base URL
R> query <- "num=10&min=100&max=200&col=2&base=10&format=plain&rnd=new"
R> txt <- paste(site, query, sep="?")            # concat url and query string
R> nums <- read.table(file=txt)                  # and read the data
R> nums                                          # and show it
   V1  V2
1 165 143
2 107 118
3 103 132
4 191 100
5 138 185
R>

Кроме того, пакет random предоставляет несколько удобных функций для доступа к random.org .


BTW-- Я хотел бы предложить , что вы должны сделать selfanswers CW , если (1) вы разместите их в кратчайшие сроки и (2) вы не делаете вопрос CW. В противном случае это выглядит так, как будто вы пытаетесь обмануть систему повторений. YMMV и все такое.
dmckee --- котенок экс-модератора

1
Это не игра в систему, это просто начало работы. Он все еще волен принять любой другой ответ.
ars

2
@ars: Он может принять это. Я также не буду пытаться заставить его использовать вики, если он не прислушается к моему совету. Но я не буду публиковать заранее подготовленный ответ, не отметив его как вики, и не буду голосовать за него без него. Принимайте это как следует.
dmckee --- котенок экс-модератора

4
@Dirk: вполне приемлемо, даже если Джефф и Джоэл поощряют его, отвечать на свой вопрос. Нет НИКАКОГО требования, даже неофициального, давать ответ CW. Вы явно не играете в систему. Еще раз, просто игнорируйте вики-полицию сообщества.
Juliet

8
Я должен согласиться с тем, что частично цель сайтов - предоставить лучшие ответы на общие проблемы и общий ресурс. Задавая вопросы и давая хороший ответ, можно укрепить тему. Это особенно полезно с новыми / маленькими тегами, такими как R.
kpierce8

35

Я нахожу , что я использую with()и все within()больше и больше. Больше $не надо засорять мой код, и не нужно начинать прикреплять объекты к пути поиска. А если серьезно, я считаю, что и with()т. Д. Делают мои сценарии анализа данных более ясными.

> df <- data.frame(A = runif(10), B = rnorm(10))
> A <- 1:10 ## something else hanging around...
> with(df, A + B) ## I know this will use A in df!
 [1]  0.04334784 -0.40444686  1.99368816  0.13871605 -1.17734837
 [6]  0.42473812  2.33014226  1.61690799  1.41901860  0.8699079

with()устанавливает среду, в которой оценивается выражение R. within()делает то же самое, но позволяет вам изменять объект данных, используемый для создания среды.

> df <- within(df, C <- rpois(10, lambda = 2))
> head(df)
           A          B C
1 0.62635571 -0.5830079 1
2 0.04810539 -0.4525522 1
3 0.39706979  1.5966184 3
4 0.95802501 -0.8193090 2
5 0.76772541 -1.9450738 2
6 0.21335006  0.2113881 4

Когда я впервые использовал within()это, я не осознавал, что вам нужно выполнить присваивание как часть оцениваемого выражения и присвоить возвращаемый объект (как указано выше), чтобы получить желаемый эффект.


34

Уловка с вводом данных = пакет RGoogleDocs

http://www.omegahat.org/RGoogleDocs/

Я обнаружил, что электронные таблицы Google - отличный способ для всех соавторов быть на одной странице. Кроме того, Google Forms позволяет собирать данные от респондентов и легко записывать их в электронную таблицу Google. Поскольку данные часто меняются и почти никогда не бывают окончательными, для R гораздо предпочтительнее читать электронную таблицу Google напрямую, чем загружать файлы CSV и считывать их.

# Get data from google spreadsheet
library(RGoogleDocs)
ps <-readline(prompt="get the password in ")
auth = getGoogleAuth("me@gmail.com", ps, service="wise")
sheets.con <- getGoogleDocsConnection(auth)
ts2=getWorksheets("Data Collection Repos",sheets.con)
names(ts2)
init.consent <-sheetAsMatrix(ts2$Sheet1,header=TRUE, as.data.frame=TRUE, trim=TRUE)

Я не могу вспомнить, какая из следующих команд, кроме одной или двух, занимает несколько секунд.

  1. getGoogleAuth

  2. getGoogleDocsConnection

  3. getWorksheets


27

Используйте обратные кавычки для ссылки на нестандартные имена.

> df <- data.frame(x=rnorm(5),y=runif(5))
> names(df) <- 1:2
> df
           1         2
1 -1.2035003 0.6989573
2 -1.2146266 0.8272276
3  0.3563335 0.0947696
4 -0.4372646 0.9765767
5 -0.9952423 0.6477714
> df$1
Error: unexpected numeric constant in "df$1"
> df$`1`
[1] -1.2035003 -1.2146266  0.3563335 -0.4372646 -0.9952423

В этом случае также будет работать df [, "1"]. Но внутри формул работают обратные галочки!

> lm(`2`~`1`,data=df)

Call:
lm(formula = `2` ~ `1`, data = df)

Coefficients:
(Intercept)          `1`  
     0.4087      -0.3440  

[Edit] Дирк спрашивает, зачем давать неверные имена? Я не знаю! Но на практике я, конечно, довольно часто сталкиваюсь с этой проблемой. Например, используя пакет hashape reshape:

> library(reshape)
> df$z <- c(1,1,2,2,2)
> recast(df,z~.,id.var="z")
Aggregation requires fun.aggregate: length used as default
  z (all)
1 1     4
2 2     6
> recast(df,z~.,id.var="z")$(all)
Error: unexpected '(' in "recast(df,z~.,id.var="z")$("
> recast(df,z~.,id.var="z")$`(all)`
Aggregation requires fun.aggregate: length used as default
[1] 4 6

Хорошо, но зачем вам заменять синтаксически допустимые имена (например, x или y) недопустимыми (например, 1 или 2), требующими обратных кавычек?
Дирк Эддельбюттель,

3
Это также полезно, read.tableкогда check.namesложно, то есть когда вы хотите работать с исходными именами столбцов.
hadley

25

Не знаю, насколько это хорошо известно, но я определенно воспользовался преимуществами передачи по ссылке в средах.

zz <- new.env()
zz$foo <- c(1,2,3,4,5)
changer <- function(blah) {
   blah$foo <- 5
}
changer(zz)
zz$foo

В этом примере непонятно, почему это может быть полезно, но если вы передаете большие объекты, это может помочь.


23

Моя новая любимая вещь - это библиотека foreach. Он позволяет вам делать все приятные вещи, но с несколько более простым синтаксисом:

list_powers <- foreach(i = 1:100) %do% {
  lp <- x[i]^i
  return (lp)
}

Самое приятное то, что если вы делаете что-то, что на самом деле требует значительного количества времени, вы можете переключиться с %do%на %dopar%(с соответствующей серверной библиотекой) для мгновенного распараллеливания, даже в кластере. Очень красиво.


19

Я занимаюсь основными манипуляциями с данными, поэтому вот две встроенные функции ( преобразование , подмножество ) и одна библиотека ( sqldf ), которые я использую ежедневно.

создать образец данных о продажах

sales <- expand.grid(country = c('USA', 'UK', 'FR'),
                     product = c(1, 2, 3))
sales$revenue <- rnorm(dim(sales)[1], mean=100, sd=10)

> sales
  country product   revenue
1     USA       1 108.45965
2      UK       1  97.07981
3      FR       1  99.66225
4     USA       2 100.34754
5      UK       2  87.12262
6      FR       2 112.86084
7     USA       3  95.87880
8      UK       3  96.43581
9      FR       3  94.59259

используйте transform (), чтобы добавить столбец

## transform currency to euros
usd2eur <- 1.434
transform(sales, euro = revenue * usd2eur)

>
  country product   revenue     euro
1     USA       1 108.45965 155.5311
2      UK       1  97.07981 139.2125
3      FR       1  99.66225 142.9157
...

используйте subset (), чтобы разрезать данные

subset(sales, 
       country == 'USA' & product %in% c(1, 2), 
       select = c('product', 'revenue'))

>
  product  revenue
1       1 108.4597
4       2 100.3475

используйте sqldf () для нарезки и агрегирования с SQL

Пакет sqldf предоставляет интерфейс SQL для фреймов данных R

##  recast the previous subset() expression in SQL
sqldf('SELECT product, revenue FROM sales \
       WHERE country = "USA" \
       AND product IN (1,2)')

>
  product  revenue
1       1 108.4597
2       2 100.3475

Выполните агрегирование или GROUP BY

sqldf('select country, sum(revenue) revenue \ 
       FROM sales \
       GROUP BY country')

>
  country  revenue
1      FR 307.1157
2      UK 280.6382
3     USA 304.6860

Для более сложных функций map-reduce для фреймов данных ознакомьтесь с пакетом plyr . И если найти себе желание вытащить свои волосы, я рекомендую проверить Манипулирование данными с R .


18
?ave

Подмножества 'x []' усредняются, причем каждое подмножество состоит из наблюдений с одинаковыми уровнями факторов. Использование: ave (x, ..., FUN = mean)

Я использую это все время. (например, в этом ответе здесь так )


чем это отличается от tapply (x, factor, fun) ??
TMS

1
@Tomas ave сохраняет порядок и длину. таким образом, вы можете, например, добавить вектор групповых средних в набор данных за один шаг.
Эдуардо Леони

18

Способ ускорить код и исключить циклы for.

вместо циклов for, которые просматривают фрейм данных в поисках значений. просто возьмите подмножество df с этими значениями, намного быстрее.

так что вместо:

for(i in 1:nrow(df)){
  if (df$column[i] == x) {
    df$column2[i] <- y
    or any other similiar code
  }
}

сделай что-нибудь вроде этого:

df$column2[df$column1 == x] <- y

эта базовая концепция применяется очень часто и является отличным способом избавиться от циклов for.


11
Здесь есть небольшая ловушка, которая меня все время ловила. Если df $ column1 содержит значения NA, при подмножестве с использованием == будут извлечены любые значения, равные x, и любые NA. Чтобы этого избежать, используйте «% в%» вместо «==».
Мэтт Паркер,

Мэтт, ты абсолютно прав, и я ненавижу это, но мне нравится твой метод. Я обычно проверяю столбец на наличие НП, а затем удаляю их с помощью быстрой функции, которую я сделал, которая берет столбец фрейма данных и возвращает фрейм данных минус строки с НП только в этом столбце.
Дэн

по сути, я сокращаю фрейм данных до столбцов, в которых мне нужны значения, затем использую na.omit для получения правильных строк, а затем подмножество исходного набора данных только с этими строками. Простое использование na.omit удалит любую строку с любым NA, хотя я могу ошибаться.
Дэн

16

Иногда требуется rbindнесколько фреймов данных. do.call()позволит вам это сделать (кто-то должен был объяснить это мне, когда я задал этот вопрос при связывании, поскольку его использование не является очевидным).

foo <- list()

foo[[1]] <- data.frame(a=1:5, b=11:15)
foo[[2]] <- data.frame(a=101:105, b=111:115)
foo[[3]] <- data.frame(a=200:210, b=300:310)

do.call(rbind, foo)

Хорошее решение: я считаю, что это часто проще, чем использовать unsplit.
Ричи Коттон

16

В R программировании (не интерактивные сеансы), я использую if (bad.condition) stop("message")в много . Каждая функция начинается с нескольких из них, и, работая над вычислениями, я тоже добавляю их. Полагаю, я привык к использованию assert()в C. Преимущества двоякие. Во-первых, с этими проверками намного быстрее получить рабочий код. Во-вторых, что, вероятно, более важно, работать с существующим кодом намного проще, если вы видите эти проверки на каждом экране в редакторе. Вам не придется задаваться вопросом x>0, или доверять комментарию о том, что это ... вы узнаете с первого взгляда, что это так.

PS. мой первый пост здесь. Быть нежным!


12
Неплохая привычка, и R предлагает еще один способ: stopfifnot(!bad.condition)более лаконичный.
Dirk Eddelbuettel 05

13

Эта traceback()функция необходима, когда у вас где-то есть ошибка, и вы не можете ее сразу понять. Он напечатает трассировку стека, что очень полезно, поскольку R по умолчанию не очень подробный.

Затем настройка options(error=recover)позволит вам «войти» в функцию, вызывающую ошибку, и попытаться понять, что именно происходит, как если бы вы полностью контролировали это и могли бы указать browser()в нем.

Эти три функции действительно могут помочь отладить ваш код.


1
options(error=recover)это мой любимый метод отладки.
Джошуа Ульрих,

12

Я действительно удивлен, что никто не написал о apply, tapply, lapply и sapply. Общее правило, которое я использую при работе с R, заключается в том, что если у меня есть цикл for, который выполняет обработку данных или моделирование, я пытаюсь выделить его и заменить на * apply. Некоторые люди уклоняются от функций * apply, потому что они думают, что можно передавать только функции с одним параметром. Ничего не может быть дальше от истины! Подобно передаче функций с параметрами в качестве объектов первого класса в Javascript, вы делаете это в R с анонимными функциями. Например:

 > sapply(rnorm(100, 0, 1), round)
  [1]  1  1  0  1  1 -1 -2  0  2  2 -2 -1  0  1 -1  0  1 -1  0 -1  0  0  0  0  0
 [26]  2  0 -1 -2  0  0  1 -1  1  5  1 -1  0  1  1  1  2  0 -1  1 -1  1  0 -1  1
 [51]  2  1  1 -2 -1  0 -1  2 -1  1 -1  1 -1  0 -1 -2  1  1  0 -1 -1  1  1  2  0
 [76]  0  0  0 -2 -1  1  1 -2  1 -1  1  1  1  0  0  0 -1 -3  0 -1  0  0  0  1  1


> sapply(rnorm(100, 0, 1), round(x, 2)) # How can we pass a parameter?
Error in match.fun(FUN) : object 'x' not found


# Wrap your function call in an anonymous function to use parameters
> sapply(rnorm(100, 0, 1), function(x) {round(x, 2)})
  [1] -0.05 -1.74 -0.09 -1.23  0.69 -1.43  0.76  0.55  0.96 -0.47 -0.81 -0.47
 [13]  0.27  0.32  0.47 -1.28 -1.44 -1.93  0.51 -0.82 -0.06 -1.41  1.23 -0.26
 [25]  0.22 -0.04 -2.17  0.60 -0.10 -0.92  0.13  2.62  1.03 -1.33 -1.73 -0.08
 [37]  0.45 -0.93  0.40  0.05  1.09 -1.23 -0.35  0.62  0.01 -1.08  1.70 -1.27
 [49]  0.55  0.60 -1.46  1.08 -1.88 -0.15  0.21  0.06  0.53 -1.16 -2.13 -0.03
 [61]  0.33 -1.07  0.98  0.62 -0.01 -0.53 -1.17 -0.28 -0.95  0.71 -0.58 -0.03
 [73] -1.47 -0.75 -0.54  0.42 -1.63  0.05 -1.90  0.40 -0.01  0.14 -1.58  1.37
 [85] -1.00 -0.90  1.69 -0.11 -2.19 -0.74  1.34 -0.75 -0.51 -0.99 -0.36 -1.63
 [97] -0.98  0.61  1.01  0.55

# Note that anonymous functions aren't being called, but being passed.
> function() {print('hello #rstats')}()
function() {print('hello #rstats')}()
> a = function() {print('hello #rstats')}
> a
function() {print('hello #rstats')}
> a()
[1] "hello #rstats"

(Для тех, кто следит за #rstats, я также разместил это там).

Помните, используйте apply, sapply, lapply, tapply и do.call! Воспользуйтесь векторизацией R. Вы никогда не должны подходить к кучке кода R и видеть:

N = 10000
l = numeric()
for (i in seq(1:N)) {
    sim <- rnorm(1, 0, 1)
    l <- rbind(l, sim)
}

Мало того, что это не векторизовано, но и структура массива в R не увеличивается, как в Python (удвоение размера, когда пространство заканчивается, IIRC). Таким образом, каждый шаг rbind должен сначала вырасти l настолько, чтобы принять результаты от rbind (), а затем скопировать все содержимое предыдущего l. Ради интереса попробуйте описанное выше в R. Обратите внимание, сколько времени это займет (вам даже не понадобится Rprof или какая-либо функция синхронизации). Тогда попробуй

N=10000
l <- rnorm(N, 0, 1)

Лучше, чем первая версия:

N = 10000
l = numeric(N)
for (i in seq(1:N)) {
    sim <- rnorm(1, 0, 1)
    l[i] <- sim
}

apply, sapply, lapply и tapply полезны. Если вы хотите передать параметры именованной функции, например round, вы можете просто передать их вместе с apply вместо написания анонимной функции. Попробуйте «sapply (rnorm (10, 0, 1), round, digits = 2)», который выводит «[1] -0,29 0,29 1,31 -0,06 -1,90 -0,84 0,21 0,02 0,23 -1,10».
Daniel

11

По совету Дирка выкладываю единичные примеры. Я надеюсь, что они не слишком "милые" [умные, но мне все равно] или тривиальные для этой аудитории.

Линейные модели - это хлеб с маслом R. Когда число независимых переменных велико, у каждого есть два варианта. Во-первых, он использует lm.fit (), который получает матрицу проектирования x и ответ y в качестве аргументов, аналогично Matlab. Недостатком этого подхода является то, что возвращаемое значение представляет собой список объектов (подогнанные коэффициенты, остатки и т. Д.), А не объект класса «lm», который можно красиво резюмировать, использовать для прогнозирования, пошагового выбора и т. Д. Второе подход заключается в создании формулы:

> A
           X1         X2          X3         X4         y
1  0.96852363 0.33827107 0.261332257 0.62817021 1.6425326
2  0.08012755 0.69159828 0.087994158 0.93780481 0.9801304
3  0.10167545 0.38119304 0.865209832 0.16501662 0.4830873
4  0.06699458 0.41756415 0.258071616 0.34027775 0.7508766
   ...

> (f=paste("y ~",paste(names(A)[1:4],collapse=" + ")))
[1] "y ~ X1 + X2 + X3 + X4"

> lm(formula(f),data=A)

Call:
lm(formula = formula(f), data = A)

Coefficients:
(Intercept)           X1           X2           X3           X4  
    0.78236      0.95406     -0.06738     -0.43686     -0.06644  

Как насчет того, чтобы выбрать по одному для каждого сообщения и проиллюстрировать его примером? Затем мы можем продолжать работать несколько дней подряд и публиковать новые примеры с новыми командами ... [BTW: Насколько я помню, вам понадобится as.formula (paste (...)) для использования формулы. ]
Дирк Эддельбюттель

Вам не нужно явно создавать формулу для покрытия всех столбцов, так как форма «y ~. - 1» покрывает это. Знак "." означает «все столбцы, кроме зависимой переменной, а« - 1 »исключает константу, как в вашем примере.
Дирк Эддельбюттель,

Это верно для этого конкретного примера, но для X с ncols >> nrows я часто удаляю некоторые независимые переменные, особенно на заключительных этапах анализа. В этом случае по-прежнему удобно создавать формулу из имен фреймов данных.
gappy

10

Вы можете присвоить значение, возвращаемое из блока if-else.

Вместо, например,

condition <- runif(1) > 0.5
if(condition) x <- 1 else x <- 2

ты можешь сделать

x <- if(condition) 1 else 2

Как это работает - глубокая магия.


6
Вы также можете сделать это как x <- ifelse (condition, 1, 2), и в этом случае каждый компонент векторизуется.
Шейн

Шейн, вы могли бы, но если вы не очень глубоко разбираетесь в том, что делает ifelse (), вам, вероятно, не стоит! Это легко неправильно понять ...
Харлан,

Что в этом волшебного? Именно так if-then-elseвыражения работают на любом функциональном языке (не путать с if-then-else операторами ). Очень похоже на тернарный ?:оператор C-подобных языков.
Фрэнк

10

Как полный новичок в R и новичок в статистике, я люблю unclass() печатать все элементы фрейма данных как обычный список.

Очень удобно просматривать полный набор данных за один раз, чтобы быстро выявить любые потенциальные проблемы.


9

CrossTable()из gmodelsпакета обеспечивает легкий доступ к кросс-таблицам в стиле SAS и SPSS, а также к обычным тестам (Chisq, McNemar и т. д.). По сути, это xtabs()навороченный вывод и некоторые дополнительные тесты, но он упрощает обмен выводами с язычниками.


Ницца!! Я довольно часто использую гмодели, но пропустил этот
Абхиджит

Хороший ответ, все, что может удержать меня от чрезмерного объяснения таблиц с язычниками, - это хорошее использование времени.
Stedy

7

Определенно system(). Возможность иметь доступ ко всем инструментам unix (по крайней мере, под Linux / MacOSX) из среды R быстро стала бесценной в моем повседневном рабочем процессе.


1
Это связано с моим предыдущим комментарием о соединениях: вы также можете использовать pipe () для передачи данных из команд Unix или в них. См. help(connections)Подробности и примеры.
Дирк Эддельбюттель,

6

Вот неприятный обходной путь для преобразования коэффициента в числовой. (Аналогично и для других типов данных)

old.var <- as.numeric(levels(old.var))[as.numeric(old.var)]

2
Возможно, вы имели в виду вектор «в персонажа». В этом случае "as.character (old.var)" проще.
Дирк Эддельбюттель

1
Я всегда считал этот совет (который можно прочесть с коэффициентом?) Ошибочным. Вы должны быть уверены, что old.var является фактором, и это будет зависеть от параметров, которые вы установили для сеанса R. Использование as.numeric (as.character (old.var)) безопаснее и чище.
Эдуардо Леони

На самом деле не стоит отрицательного голоса, но все равно. У меня это работает.
Райан Р. Росарио

Райан - Не могли бы вы исправить свой код? Если old.var <- коэффициент (1: 2); ваш код даст [1] «1» «2» (не числовой.) возможно, вы имели в виду as.numeric (levels (old.var) [old.var])?
Эдуардо Леони

3
Или чуть более эффективно:as.numeric(levels(old.var))[old.var]
hadley

6

Хотя этот вопрос поднимался некоторое время, я недавно обнаружил в блоге SAS и R отличный трюк для использования этой команды cut. Команда используется для разделения данных на категории, и я буду использовать набор данных iris в качестве примера и разделить его на 10 категорий:

> irisSL <- iris$Sepal.Length
> str(irisSL)
 num [1:150] 5.1 4.9 4.7 4.6 5 5.4 4.6 5 4.4 4.9 ...
> cut(irisSL, 10)
  [1] (5.02,5.38] (4.66,5.02] (4.66,5.02] (4.3,4.66]  (4.66,5.02] (5.38,5.74] (4.3,4.66]  (4.66,5.02] (4.3,4.66]  (4.66,5.02]
 [11] (5.38,5.74] (4.66,5.02] (4.66,5.02] (4.3,4.66]  (5.74,6.1]  (5.38,5.74] (5.38,5.74] (5.02,5.38] (5.38,5.74] (5.02,5.38]
 [21] (5.38,5.74] (5.02,5.38] (4.3,4.66]  (5.02,5.38] (4.66,5.02] (4.66,5.02] (4.66,5.02] (5.02,5.38] (5.02,5.38] (4.66,5.02]
 [31] (4.66,5.02] (5.38,5.74] (5.02,5.38] (5.38,5.74] (4.66,5.02] (4.66,5.02] (5.38,5.74] (4.66,5.02] (4.3,4.66]  (5.02,5.38]
 [41] (4.66,5.02] (4.3,4.66]  (4.3,4.66]  (4.66,5.02] (5.02,5.38] (4.66,5.02] (5.02,5.38] (4.3,4.66]  (5.02,5.38] (4.66,5.02]
 [51] (6.82,7.18] (6.1,6.46]  (6.82,7.18] (5.38,5.74] (6.46,6.82] (5.38,5.74] (6.1,6.46]  (4.66,5.02] (6.46,6.82] (5.02,5.38]
 [61] (4.66,5.02] (5.74,6.1]  (5.74,6.1]  (5.74,6.1]  (5.38,5.74] (6.46,6.82] (5.38,5.74] (5.74,6.1]  (6.1,6.46]  (5.38,5.74]
 [71] (5.74,6.1]  (5.74,6.1]  (6.1,6.46]  (5.74,6.1]  (6.1,6.46]  (6.46,6.82] (6.46,6.82] (6.46,6.82] (5.74,6.1]  (5.38,5.74]
 [81] (5.38,5.74] (5.38,5.74] (5.74,6.1]  (5.74,6.1]  (5.38,5.74] (5.74,6.1]  (6.46,6.82] (6.1,6.46]  (5.38,5.74] (5.38,5.74]
 [91] (5.38,5.74] (5.74,6.1]  (5.74,6.1]  (4.66,5.02] (5.38,5.74] (5.38,5.74] (5.38,5.74] (6.1,6.46]  (5.02,5.38] (5.38,5.74]
[101] (6.1,6.46]  (5.74,6.1]  (6.82,7.18] (6.1,6.46]  (6.46,6.82] (7.54,7.9]  (4.66,5.02] (7.18,7.54] (6.46,6.82] (7.18,7.54]
[111] (6.46,6.82] (6.1,6.46]  (6.46,6.82] (5.38,5.74] (5.74,6.1]  (6.1,6.46]  (6.46,6.82] (7.54,7.9]  (7.54,7.9]  (5.74,6.1] 
[121] (6.82,7.18] (5.38,5.74] (7.54,7.9]  (6.1,6.46]  (6.46,6.82] (7.18,7.54] (6.1,6.46]  (5.74,6.1]  (6.1,6.46]  (7.18,7.54]
[131] (7.18,7.54] (7.54,7.9]  (6.1,6.46]  (6.1,6.46]  (5.74,6.1]  (7.54,7.9]  (6.1,6.46]  (6.1,6.46]  (5.74,6.1]  (6.82,7.18]
[141] (6.46,6.82] (6.82,7.18] (5.74,6.1]  (6.46,6.82] (6.46,6.82] (6.46,6.82] (6.1,6.46]  (6.46,6.82] (6.1,6.46]  (5.74,6.1] 
10 Levels: (4.3,4.66] (4.66,5.02] (5.02,5.38] (5.38,5.74] (5.74,6.1] (6.1,6.46] (6.46,6.82] (6.82,7.18] ... (7.54,7.9]

5

Еще одна уловка. Некоторые пакеты, как glmnet, только взять в качестве входов матрицы дизайна и переменный отклик. Если кто-то хочет подогнать модель со всеми взаимодействиями между элементами, он не может использовать формулу «y ~. ^ 2». Использование expand.grid()позволяет нам использовать мощные возможности индексирования массивов и векторных операций R.

interArray=function(X){
    n=ncol(X)
    ind=expand.grid(1:n,1:n)
    return(X[,ind[,1]]*X[,ind[,2]])
}

> X
          X1         X2
1 0.96852363 0.33827107
2 0.08012755 0.69159828
3 0.10167545 0.38119304
4 0.06699458 0.41756415
5 0.08187816 0.09805104

> interArray(X)
           X1          X2        X1.1        X2.1
1 0.938038022 0.327623524 0.327623524 0.114427316
2 0.006420424 0.055416073 0.055416073 0.478308177
3 0.010337897 0.038757974 0.038757974 0.145308137
4 0.004488274 0.027974536 0.027974536 0.174359821
5 0.006704033 0.008028239 0.008028239 0.009614007

3
Если функция моделирования не принимает формулу (что бывает очень редко!), Не лучше ли построить матрицу дизайна с помощью model.matrix?
Хэдли

Хороший. Я не знал о существовании этой функции. Вышеупомянутая функция эквивалентна model.matrix (~. ^ 2 -1, X) Но что касается передачи матриц, помимо glmnet, я часто передаю указатели на массивы пользовательским функциям C. В самом деле, я бы не знал, как передать формулу функции. У вас есть игрушечный пример?
gappy

5

Один из моих любимых, если не сказать несколько неортодоксальных приемов - это использование eval()и parse(). Этот пример, возможно, показывает, как это может быть полезно

NY.Capital <- 'Albany'
state <- 'NY'
parameter <- 'Capital'
eval(parse(text=paste(state, parameter, sep='.')))

[1] "Albany"

Ситуации такого типа возникают чаще, и их использование eval()и parse()может помочь в их решении. Конечно, я приветствую любые отзывы об альтернативных способах написания кода.


1
Это также можно сделать с помощью именованных векторных элементов.
Дирк Эддельбюттель,

3
library (fortunes); fortune (106) Если ответ - parse (), вы обычно должны переосмыслить вопрос. - Thomas Lumley R-help (февраль 2005 г.)
Эдуардо Леони

Вот пример, в котором могут быть полезны eval () и parse (). Это включает в себя пакет Bioconductor, например hgu133a.db, и где вы пытаетесь получить различную информацию об идентификаторе набора зондов. Например: параметр библиотеки (hgu133a.db) <- 'SYMBOL' mget ('202431_s_at', env = eval (parse (text = paste ('hgu133a', parameter, sep = '')))) параметр <- 'ENTREZID 'mget (' 202431_s_at ', env = eval (parse (text = paste (' hgu133a ', parameter, sep =' '))))
andrewj

Как говорит Дирк, это лучше делать с помощью именованных векторных элементов или `get (paste (state, parameter, sep = '.'))`
Хэдли

@ Хэдли, не знала, что таким образом можно использовать get (). Спасибо.
andrewj

5

set.seed() устанавливает состояние генератора случайных чисел.

Например:

> set.seed(123)
> rnorm(1)
[1] -0.5604756
> rnorm(1)
[1] -0.2301775
> set.seed(123)
> rnorm(1)
[1] -0.5604756

очень полезно с примерами, в которых используются случайные функции ... помогает собрать всех на одной странице
JD Long

5

Для тех, кто пишет на C, вызов из R: .Internal(inspect(...))удобен. Например:

> .Internal(inspect(quote(a+2)))
  @867dc28 06 LANGSXP g0c0 [] 
  @8436998 01 SYMSXP g1c0 [MARK,gp=0x4000] "+"
  @85768b0 01 SYMSXP g1c0 [MARK,NAM(2)] "a"
  @8d7bf48 14 REALSXP g0c1 [] (len=1, tl=0) 2

4

d = '~ / Код R / Библиотека /'

files = list.files (d, '. r $')

for (f в файлах) {if (! (f == 'mysource.r')) {print (paste ('Sourcing', f)) source (paste (d, f, sep = ''))}}

Я использую приведенный выше код для получения всех файлов в каталоге при запуске с различными служебными программами, которые я использую в моем интерактивном сеансе с R. Я уверен, что есть лучшие способы, но я считаю его полезным для своей работы. Строка, которая делает это, выглядит следующим образом.

источник ("~ / R Code / Library / mysource.r")


6
Не делай этого. Напишите пакет.
Dirk Eddelbuettel

Спасибо. Я просматривал пару веток на roxygen, и мне кажется, что я, вероятно, нахожусь на том уровне, на котором я должен попробовать написать простой пакет для самостоятельного использования.
mcheema

3

Для выполнения операции с рядом переменных в кадре данных. Это украдено из subset.data.frame.

get.vars<-function(vars,data){
    nl <- as.list(1L:ncol(data))
    names(nl) <- names(data)
    vars <- eval(substitute(vars), nl, parent.frame())
    data[,vars]
    #do stuff here
}

get.vars(c(cyl:hwy,class),mpg)

1
Поначалу это кажется крутым, но в конечном итоге такой код доставит вам немало проблем. Всегда лучше быть откровенным.
Хэдли

хм, в последнее время я довольно часто использую этот трюк. Не могли бы вы подробнее рассказать о его безграничной проблеме?
Ian Fellows

Может быть, Хадли предлагает вместо этого использовать пакет plyr?
Кристофер Дюбуа,

3
Нет, это не завуалированное предложение использовать вместо этого plyr. Основная проблема вашего кода в том, что он семантически ленив - вместо того, чтобы заставлять пользователя явно объяснять то, что он хочет, вы делаете некую «магию», чтобы угадать. Проблема в том, что это делает функцию очень сложной для программирования - т.е. трудно написать функцию, которая вызывает, get.varsне перепрыгивая через множество обручей.
Хэдли

3

Я уже публиковал это однажды, но я так часто им пользуюсь, что подумал, что выложу еще раз. Это всего лишь небольшая функция, возвращающая имена и номера позиций data.frame. В этом нет ничего особенного, чтобы быть уверенным, но я почти никогда не прохожу через сеанс, не используя его несколько раз.

##creates an object from a data.frame listing the column names and location

namesind = function (df) {

temp1=names(df)
temp2=seq(1,length(temp1))
temp3=data.frame(temp1,temp2)
names(temp3)=c("VAR","COL")
return(temp3)
rm(temp1,temp2,temp3)

}

ni <- namesind


4
Это действительно однострочник:data.frame(VAR = names(df), COL = seq_along(df))
Хэдли,

очень элегантно, может быть, я переключу его на ni <- function (df) {data.frame (VAR = names (df), COL = seq_along (df))}
kpierce8

1
Я использую: data.frame (colnames (the.df))
Тал Галили
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.