Nano Hash - криптовалюты, майнинг, программирование

Более эффективные способы использования R, чем циклы for

Я относительно новичок в R, поэтому мне очень жаль, если есть очевидный ответ на этот вопрос. Я рассмотрел другие вопросы и думаю, что «применить» - это ответ, но я не могу понять, как его использовать в этом случае.

У меня есть лонгитюдный опрос, участники которого приглашаются каждый год. В какие-то годы они не принимают участия, а иногда умирают. Мне нужно определить, какие участники принимали участие в последовательной «полосе» с момента начала опроса (т.е. если они останавливаются, они останавливаются навсегда).

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

В этом примере ИСТИНА означает, что они участвовали в этом году. Цикл создает два вектора - «последний год» для последнего года, в котором они участвовали, и «полосу», чтобы показать, завершили ли они все годы до последнего года (т. Е. Случаи 1, 3 и 5).

dat <- data.frame(ids = 1:5, "1999" = c(T, T, T, F, T), "2000" = c(T, F, T, F, T), "2001" = c(T, T, T, T, T), "2002" = c(F, T, T, T, T), "2003" = c(F, T, T, T, F))
finalyear <- NULL
streak <- NULL
for (i in 1:nrow(dat)) {
    x <- as.numeric(dat[i,2:6])
    y <- max(grep(1, x))
    finalyear[i] <- y
    streak[i] <- sum(x) == y
}
dat$finalyear <- finalyear
dat$streak <- streak

Спасибо!

04.09.2015

  • Множество ответов - кто-нибудь хочет создать больший набор данных и сравнить их? Насколько велик набор данных, чтобы его можно было сделать репрезентативным набором тестов для сравнительного анализа? 04.09.2015
  • Есть около 250 000 случаев и 25 лет. Все ответы ниже решают мою проблему - всем спасибо! Если людям интересно, я мог бы сделать репрезентативный набор данных для тестирования различных подходов. 04.09.2015

Ответы:


1

Мы могли бы использовать max.col и rowSums как vectorized подход.

dat$finalyear <- max.col(dat[-1], 'last')

Если есть строки без значений TRUE, мы можем убедиться, что вернули 0 для этой строки, умножив на двойное отрицание rowSums. FALSE будет приведен к 0, и умножение на 0 вернет 0 для этой строки.

dat$finalyear <- max.col(dat[-1], 'last')*!!rowSums(dat[-1])

Затем мы создаем столбец «полоса», сравнивая rowSums столбцов 2: 6 с столбцом «последний год».

dat$streak <-  rowSums(dat[,2:6])==dat$finalyear
dat
#   ids X1999 X2000 X2001 X2002 X2003 finalyear streak
#1   1  TRUE  TRUE  TRUE FALSE FALSE         3   TRUE
#2   2  TRUE FALSE  TRUE  TRUE  TRUE         5  FALSE
#3   3  TRUE  TRUE  TRUE  TRUE  TRUE         5   TRUE
#4   4 FALSE FALSE  TRUE  TRUE  TRUE         5  FALSE
#5   5  TRUE  TRUE  TRUE  TRUE FALSE         4   TRUE

Или однострочный код (он мог поместиться в одну строку, но решил сделать это очевидным двумя строками), предложенный @ColonelBeauvel

library(dplyr)
mutate(dat, finalyear=max.col(dat[-1], 'last'), 
            streak=rowSums(dat[-1])==finalyear)
04.09.2015
  • Лучший краткий и векторизованный ответ. +1 04.09.2015
  • один лайнер с mutate(dat, finalyear=max.col(dat[-1], 'last'), streak=rowSums(dat[-1])==finalyear) 04.09.2015

  • 2

    Циклы For по своей сути не являются плохими в R, но они работают медленно, если вы увеличиваете векторы итеративно (как вы это делаете). Часто есть способы сделать что-то лучше. Пример решения только с apply-функциями:

    dat$finalyear <- apply(dat[,2:6],MARGIN=1,function(x){max(which(x))})
    dat$streak <-  apply(dat[,2:7],MARGIN=1,function(x){sum(x[1:5])==x[6]})
    

    Или вариант 2, на основе комментария @Spacedman:

    dat$finalyear <- apply(dat[,2:6],MARGIN=1,function(x){max(which(x))})
    dat$streak <-  apply(dat[,2:6],MARGIN=1,function(x){max(which(x))==sum(x)})
    
    > dat
      ids X1999 X2000 X2001 X2002 X2003 finalyear streak
    1   1  TRUE  TRUE  TRUE FALSE FALSE         3   TRUE
    2   2  TRUE FALSE  TRUE  TRUE  TRUE         5  FALSE
    3   3  TRUE  TRUE  TRUE  TRUE  TRUE         5   TRUE
    4   4 FALSE FALSE  TRUE  TRUE  TRUE         5  FALSE
    5   5  TRUE  TRUE  TRUE  TRUE FALSE         4   TRUE
    
    04.09.2015
  • Аккуратно, но будьте осторожны, это зависит от того, что finalyear добавляется сразу после данных true / false, в данном случае в столбце 7. 04.09.2015
  • Спасибо. Я сомневался, стоит ли мне делать это таким образом или дважды вызывать max (which (x)). Буду редактировать. 04.09.2015

  • 3

    Вот решение с dplyr и tidyr.

    gather(data = dat,year,value,-ids) %>%
      mutate(year=as.integer(gsub("X","",year))) %>%
      group_by(ids) %>%
      summarize(finalyear=last(year[value]),
                streak=!any(value[first(year):finalyear] == FALSE))
    

    вывод

      ids finalyear streak
    1   1      2001   TRUE
    2   2      2003  FALSE
    3   3      2003   TRUE
    4   4      2003  FALSE
    5   5      2002   TRUE
    
    04.09.2015

    4

    Вот базовая версия, использующая apply для перебора строк и rle, чтобы увидеть, как часто изменяется состояние. Кажется, что ваше состояние эквивалентно состоянию, начинающемуся с TRUE и только когда-либо изменяющемуся на FALSE самое большее один раз, поэтому я проверяю, что rle короче 3, а первое значение - TRUE:

    > dat$streak = apply(dat[,2:6],1,function(r){r[1] & length(rle(r)$length)<=2})
    > 
    > dat
      ids X1999 X2000 X2001 X2002 X2003 streak
    1   1  TRUE  TRUE  TRUE FALSE FALSE   TRUE
    2   2  TRUE FALSE  TRUE  TRUE  TRUE  FALSE
    3   3  TRUE  TRUE  TRUE  TRUE  TRUE   TRUE
    4   4 FALSE FALSE  TRUE  TRUE  TRUE  FALSE
    5   5  TRUE  TRUE  TRUE  TRUE FALSE   TRUE
    

    Вероятно, есть множество способов отработать finalyear, это просто находит последний элемент каждой строки, который равен TRUE:

    > dat$finalyear = apply(dat[,2:6], 1, function(r){max(which(r))})
    > dat
      ids X1999 X2000 X2001 X2002 X2003 streak finalyear
    1   1  TRUE  TRUE  TRUE FALSE FALSE   TRUE         3
    2   2  TRUE FALSE  TRUE  TRUE  TRUE  FALSE         5
    3   3  TRUE  TRUE  TRUE  TRUE  TRUE   TRUE         5
    4   4 FALSE FALSE  TRUE  TRUE  TRUE  FALSE         5
    5   5  TRUE  TRUE  TRUE  TRUE FALSE   TRUE         4
    
    04.09.2015
    Новые материалы

    Кластеризация: более глубокий взгляд
    Кластеризация — это метод обучения без учителя, в котором мы пытаемся найти группы в наборе данных на основе некоторых известных или неизвестных свойств, которые могут существовать. Независимо от..

    Как написать эффективное резюме
    Предложения по дизайну и макету, чтобы представить себя профессионально Вам не позвонили на собеседование после того, как вы несколько раз подали заявку на работу своей мечты? У вас может..

    Частный метод Python: улучшение инкапсуляции и безопасности
    Введение Python — универсальный и мощный язык программирования, известный своей простотой и удобством использования. Одной из ключевых особенностей, отличающих Python от других языков, является..

    Как я автоматизирую тестирование с помощью Jest
    Шутка для победы, когда дело касается автоматизации тестирования Одной очень важной частью разработки программного обеспечения является автоматизация тестирования, поскольку она создает..

    Работа с векторными символическими архитектурами, часть 4 (искусственный интеллект)
    Hyperseed: неконтролируемое обучение с векторными символическими архитектурами (arXiv) Автор: Евгений Осипов , Сачин Кахавала , Диланта Хапутантри , Тимал Кемпития , Дасвин Де Сильва ,..

    Понимание расстояния Вассерштейна: мощная метрика в машинном обучении
    В обширной области машинного обучения часто возникает необходимость сравнивать и измерять различия между распределениями вероятностей. Традиционные метрики расстояния, такие как евклидово..

    Обеспечение масштабируемости LLM: облачный анализ с помощью AWS Fargate и Copilot
    В динамичной области искусственного интеллекта все большее распространение получают модели больших языков (LLM). Они жизненно важны для различных приложений, таких как интеллектуальные..