Який найкорисніший трюк R? [зачинено]


88

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

Один пункт на пост, і ми побачимо, чи отримаємо ми переможця за допомогою голосів.

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


8
@Dirk: "wiki-спільнота" означає "спільнота", що не є синонімом "питання опитування". Не слухайте спільноту wiki police.
Джульєтта


8
Знову CW знущання. Я побачу ваш мета-SO та підніму вас: meta.stackexchange.com/questions/392/…
ars

13
@ars: це питання, на яке немає однозначної відповіді . Ergo зроби це CW.
dmckee --- екс-модератор кошеня

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

Відповіді:


64

str() повідомляє вам структуру будь-якого об'єкта.


Python використовує dir()- має більше сенсу.
Hamish Grubijan

17
Ах, strце також скорочення для stringбагатьох мов.
Hamish Grubijan

Чому ні class()? Здається, це виявляє подібний тип інформації. Чому існують дві подібні команди?
hhh

1
class()є лише невеликою частиною інформації, яка str()відображається
Гадлі

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: Він може прийняти цей. Я також не буду намагатися змусити його вікі-тезувати, якщо він виграє; не послухаюся моєї поради. Але я не опублікую підготовлену самовідповідь, не позначивши її wiki, і без неї також не проголосую. Прийміть це за те, що воно того варте.
dmckee --- екс-модератор кошеня

4
@Dirk: цілком прийнятно, навіть закликане Джеффом та Джоелем, відповісти на власне запитання. Немає вимоги, навіть неформальної, щоб зробити свою відповідь CW. Ви явно не граєте в систему. Ще раз, просто ігноруйте спільноту wiki police.
Джульєтта

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, ніж для futz із завантаженням файлів 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  

[Редагувати] Дірк запитує, чому можна давати недійсні імена? Не знаю! Але я, звичайно, стикаюся з цією проблемою на практиці досить часто. Наприклад, використовуючи пакет переробки Hadley:

> 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), що вимагають зворотних позначок?
Dirk Eddelbuettel

3
Це також корисно, read.tableколи check.namesfalse - тобто, коли ви хочете працювати з вихідними іменами стовпців.
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(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

Щоб отримати більш витончену функцію зменшення карти на фреймах даних, перегляньте пакет plyr . І якщо знайти собі бажання витягнути своє волосся, я рекомендую перевірити Маніпулювання даними з R .


18
?ave

Підмножини 'x []' усереднені, де кожна підмножина складається з тих спостережень з однаковими рівнями факторів. Використання: ave (x, ..., FUN = середнє)

Я постійно ним користуюся. (наприклад, у цій відповіді тут на так )


чим це відрізняється від tapply (х, коефіцієнт, задоволення) ??
TMS

1
@Tomas ave зберігає порядок і довжину. так що ви можете, наприклад, одним кроком додати вектор набору груп до набору даних.
Eduardo Leoni

18

Шлях для прискорення коду та усунення циклів.

замість для циклів, які циклічно проходять через фрейм даних, шукаючи значення. просто візьміть підмножину 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

Ця основна концепція застосовується надзвичайно часто і є чудовим способом позбутися від циклів


11
Тут є маленька пастка, яка мене весь час наздоганяла. Якщо df $ column1 містить значення NA, підмножина за допомогою == витягне будь-які значення, що дорівнюють x, і будь-які NA. Щоб цього уникнути, використовуйте "% у%" замість "==".
Метт Паркер,

Метт, ти абсолютно правий, і це те, що я ненавиджу, мені подобається твій метод. Зазвичай я перевіряю стовпець на наявність NA, а потім видаляю їх за допомогою швидкої функції, яку я зробив, яка бере стовпець фрейму даних і повертає фрейм даних мінус рядки з НС лише в цьому стовпці.
Dan

по суті, я обробляю фрейм даних до стовпців, мені потрібно мати значення, потім використовую 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)який є більш стислим.
Дірк Еддельбюттель

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".
Даніель

11

За порадою Дірка я публікую окремі приклади. Сподіваюся, вони не надто "милі" [розумні, але мені все одно] чи тривіальні для цієї аудиторії.

Лінійні моделі - це хліб і масло Р. Коли кількість незалежних змінних велика, можна вибрати два варіанти. Перший - це використовувати 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  

Як щодо того, якщо ви оберете одну для публікації та проілюструєте приклад? Потім ми можемо продовжувати цілі дні і публікувати нові приклади з новими командами ... [ДО: Як я пам’ятаю, вам потрібна as.formula (вставити (...)) для використання формул. ]
Дірк Еддельбюттель

Вам не потрібно явно створювати формули, щоб охоплювати всі стовпці, оскільки форма "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 (умова, 1, 2), і в цьому випадку кожен компонент векторизується.
Шейн

Шейн, ти міг би, але якщо ти справді глибоко не перебираєш, що робить ifelse (), ти, мабуть, не повинен! Порозуміти це легко ...
Харлан,

Що в цьому чарівного? Це просто спосіб if-then-elseроботи виразів у будь-якій функціональній мові (не плутати з if-then-else твердженнями ). Дуже схожий на потрійний ?:оператор С-подібних мов.
Frank

10

Як загальний нуб до Р та новачок у статистиці, я люблю unclass() друкувати всі елементи кадру даних як звичайний список.

Дуже зручно переглянути повний набір даних за один раз, щоб швидко оглянути будь-які потенційні проблеми.


9

CrossTable()з gmodelsпакету забезпечує легкий доступ до перехресних таблиць у стилі SAS та SPSS, поряд із звичайними тестами (Chisq, McNemar та ін.). В основному, це xtabs()з вигадливим виходом та деякими додатковими тестами - але це полегшує обмін результатами з поганами.


Приємно !! Я досить багато використовую gmodels, але пропустив цей
Abhijit

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

7

Остаточно system(). Можливість отримати доступ до всіх інструментів unix (принаймні під Linux / MacOSX) із середовища R швидко стала неоціненною в моєму щоденному робочому процесі.


1
Це пов’язано з моїм попереднім коментарем про підключення: ви також можете використовувати pipe () для передачі даних від команд Unix або до них. Дивіться help(connections)подробиці та приклади.
Dirk Eddelbuettel

6

Ось прикрий обхідний спосіб перетворення коефіцієнта в числовий. (Аналогічно і для інших типів даних)

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

2
Можливо, ви мали на увазі вектор "на характер". У такому випадку "як.символ (старий.var)" простіший.
Dirk Eddelbuettel

1
Я завжди вважав цю пораду (яку можна прочитати при? Факторі) помилковою. Ви повинні бути впевнені, що old.var є фактором, і це буде залежати від параметрів, встановлених для сеансу R. Використання as.numeric (as.character (old.var)) є і безпечнішим, і чистішим.
Едуардо Леоні

Дійсно, не варто голосувати проти, але як би там не було. Це працює для мене.
Райан Р. Росаріо

Райан - Не могли б ви виправити свій код? Якщо old.var <- коефіцієнт (1: 2); ваш код дасть [1] "1" "2" (не числовий.) можливо ви мали на увазі as.numeric (рівні (старий.var) [старий.var])?
Едуардо Леоні

3
Або трохи ефективніше:as.numeric(levels(old.var))[old.var]
Хадлі

6

Незважаючи на те, що це питання виникало деякий час, я нещодавно виявив чудовий фокус у блозі SAS і R для використання команди cut. Команда використовується для поділу даних на категорії, і я буду використовувати приклад набору райдужної оболонки ока і розділити його на 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
бібліотека (fortunes); fortune (106) Якщо відповідь аналізується (), як правило, слід переосмислити питання. - Томас Ламлі R-help (лютий 2005 р.)
Едуардо Леоні

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

Як говорить Дірк, це краще робити з названими векторними елементами, або `get (paste (state, parameter, sep = '.')) '
hadley

@Hadley, не знав, що ти можеш використовувати 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 $')

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

Я використовую наведений вище код для пошуку всіх файлів у каталозі під час запуску з різними утилітами, які я використовую в інтерактивному сеансі з R. Я впевнений, що є кращі способи, але я вважаю це корисним для своєї роботи. Рядок, який робить це, такий.

джерело ("~ / R Code / Library / mysource.r")


6
Не роби цього. Напишіть пакет.
Dirk Eddelbuettel

Дякую. Я розглядав одну чи дві теми щодо кисню, і, здається, я, мабуть, на тому рівні, коли мені слід спробувати написати простий пакет для самостійного використання.
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.