Рассчитать дополнительный совокупный счет выживания - PullRequest
2 голосов
/ 07 апреля 2020

Я хочу рассчитать дополнительный совокупный счет выживаемости для последующего отображения в гистограмме (без использования ggplot). Например, подсчитайте количество элементов, сохранившихся за 4,0 года, 4,5 года, 5,0 года и т. Д.

Ввод данных - 10000 строк данных с 4 различными типами с 4 различными распределениями ожидаемой продолжительности жизни:

type <- c(rep("A",1000),        rep("B",2000),      rep("C",3000),      rep("D",4000))
age <-  c(rnorm(1000,6,0.5),    rnorm(2000,8,0.5),  rnorm(3000,10,0.5), rnorm(4000,12,0.5))
input <- data.frame(type,age,stringsAsFactors=FALSE)

Выходные данные построены с использованием диапазона для возрастных шагов:

range <- seq(floor(min(input$age)),ceiling(max(input$age)),0.5)

Я раскрываю в data.frame с диапазоном и типами:

combns <- expand.grid(age=range,type=LETTERS[1:4], stringsAsFactors=FALSE)

И затем использую функцию apply для подсчитайте общее число, превышающее каждый возрастной шаг:

CCSC.apply.all <- apply(combns[1:length(range),],1,function(x){
           sum(input$age >= x["age"]) } )   

и сгруппировано по типу:

CCSC.apply.type <- apply(combns,1,function(x){
            sum(                    
                input["age"] >= x["age"]    &
                input["type"] == x["type"]
            ) } )  

В пределах функции применения input["age"] >= x["age"] оценивается иногда неправильно. Это приводит к неправильному подсчету. В таблице ниже столбцы 2: 6 создаются с использованием apply, столбцы 7:11 с a для l oop. 2: 6 неправильные, 7:11 правильные.

> output
       range all-apply A-apply B-apply C-apply D-apply all-for A-for B-for C-for D-for
    1    4,0     10000    1000    2000    3000    4000   10000  1000  2000  3000  4000
    2    4,5     10000    1000    2000    3000    4000    9998   998  2000  3000  4000
    3    5,0     10000    1000    2000    3000    4000    9978   978  2000  3000  4000
    4    5,5     10000    1000    2000    3000    4000    9843   843  2000  3000  4000
    5    6,0     10000    1000    2000    3000    4000    9483   483  2000  3000  4000
    6    6,5     10000    1000    2000    3000    4000    9141   143  1998  3000  4000
    7    7,0     10000    1000    2000    3000    4000    8981    23  1958  3000  4000
    8    7,5     10000    1000    2000    3000    4000    8690     2  1688  3000  4000
    9    8,0     10000    1000    2000    3000    4000    8030     0  1030  3000  4000
    10   8,5     10000    1000    2000    3000    4000    7329     0   330  2999  4000
    11   9,0     10000    1000    2000    3000    4000    6989     0    43  2946  4000
    12   9,5     10000    1000    2000    3000    4000    6528     0     2  2526  4000
    13  10,0     10000    1000    2000    3000    4000    5494     0     0  1494  4000
    14  10,5      8961    1000    2000    1967    3994    4455     0     0   461  3994
    15  11,0      8485    1000    2000    1571    3914    3979     0     0    65  3914
    16  11,5      7900    1000    2000    1510    3390    3394     0     0     4  3390
    17  12,0      6515    1000    2000    1506    2009    2009     0     0     0  2009
    18  12,5      5123    1000    2000    1506     617     617     0     0     0   617
    19  13,0      4594    1000    2000    1506      88      88     0     0     0    88
    20  13,5      4513    1000    2000    1506       7       7     0     0     0     7
    21  14,0      4506    1000    2000    1506       0       0     0     0     0     0

Может кто-нибудь сказать мне, в чем проблема с моей функцией применения?

Дополнительное наблюдение: вопреки моим ожиданиям, применяется метод применения примерно в 100 раз медленнее, чем for-l oop.

Полный текст R приведен ниже:

    rm(list=ls())
    setwd("C:/R_test")
    options(OutDec= ",")    # to be deleted if not applicable for locale
    set.seed(1234)
    # creating input - data.frame 10000 data sets to be examined
    # 4 different types with 4 different normal distributions for life expectancy
    type <- c(rep("A",1000),        rep("B",2000),      rep("C",3000),      rep("D",4000))
    age <-  c(rnorm(1000,6,0.5),    rnorm(2000,8,0.5),  rnorm(3000,10,0.5), rnorm(4000,12,0.5))
    input <- data.frame(type,age,stringsAsFactors=FALSE)
    # complementary cumulative survival count (CCSC)
    range <- seq(floor(min(input$age)),ceiling(max(input$age)),0.5)     # range for evaluation
    # method "apply"
    apply_time <- system.time( {
        combns <- expand.grid(age=range,type=LETTERS[1:4], stringsAsFactors=FALSE)
        CCSC.apply.all <- apply(combns[1:length(range),],1,function(x){
            sum(input$age >= x["age"]) } )      #   count survivors of all types
        CCSC.apply.type <- apply(combns,1,function(x){
            sum(                                #   count survivors of certain type
                input["age"] >= x["age"]    &
                input["type"] == x["type"]
            ) } )
    })
    output <- data.frame(range, matrix(c(CCSC.apply.all,CCSC.apply.type), nrow=length(range)))
    # method "for loop"
    for_time <- system.time( {
        CCSC.for.all <- rep(0.0, length(range))
        CCSC.for.type <- matrix(rep(0.0, 4*length(range)),nrow=length(range))
        for(i in 1:length(range))
        {
            CCSC.for.all[i] <- sum(input$age >= range[i])
            for(j in 1:4)
            {
                CCSC.for.type[i,j] <- 
                sum(
                    input["age"] >= range[i]        &
                    input["type"] == LETTERS[j]
                )
            }
        }
    })
    output <- cbind(output,CCSC.for.all,CCSC.for.type)
    colnames(output) <- c("range",
                            "all-apply","A-apply","B-apply","C-apply","D-apply",
                            "all-for","A-for","B-for","C-for","D-for")
    cat("\ntime for apply method:    ", apply_time)
    cat("\ntime for for loop method: ", for_time, "\n\n")
    write.table(input, file = "CCSC_input.csv", sep=";", row.names=FALSE, dec=",")
    write.table(output, file = "CCSC_output.csv", sep=";", row.names=FALSE, dec=",")

Ответы [ 2 ]

0 голосов
/ 07 апреля 2020

Я изо всех сил пытался понять, что именно вы искали, поэтому извиняюсь, если я ошибся. Я пытался использовать split, чтобы упростить итерацию по переменной type, а затем использовал пакет purrr для итерации, а не семейства apply.

Будучи более явным вне итерационной функции - например, используя unique(combns$age) - я думаю, что легче понять, что повторяется. Например, в вашем исходном коде я думаю, что x ["age"] привел к символу, а не к цифре c, как вы ожидали.

К вашему сведению - различия в значениях, вероятно, связаны с использованием rnorm при генерации данных, а не при настройке начального числа.

# split input list by type
input_list <- split(input, type)

# for each type, calculate age >= each unique value of combns$age
purrr::map_df(input_list,
       .f = function(y) {
         purrr::map_dbl(unique(combns$age), 
                        .f = function(x) sum(y$age >= x))
       })

# A tibble: 21 x 4
       A     B     C     D
   <dbl> <dbl> <dbl> <dbl>
 1  1000  2000  3000  4000
 2  1000  2000  3000  4000
 3   970  2000  3000  4000
 4   841  2000  3000  4000
 5   458  2000  3000  4000
 6   149  2000  3000  4000
 7    32  1956  3000  4000
 8     2  1704  3000  4000
 9     0  1022  3000  4000
10     0   340  2997  4000
# … with 11 more rows
0 голосов
/ 07 апреля 2020

"подсчитать количество элементов, выживших 4,0 года, 4,5 года, 5,0 года и т. Д."

Код:

с использованием cut() для получения возрастных интервалов.

1. По возрасту и типу введите:

library('data.table')
df <- setDT(input)[, .N, by = .(age_range = cut(age, range, include.lowest = TRUE), type)]
df[order(age_range),]
#    age_range type    N
# 1:   (4.5,5]    A   20
# 2:   (5,5.5]    A  123
# 3:   (5.5,6]    A  337
# 4:   (6,6.5]    A  352
# 5:   (6,6.5]    B    6
# 6:   (6.5,7]    A  151
# 7:   (6.5,7]    B   47
# 8:   (7,7.5]    A   16
# 9:   (7,7.5]    B  277
# 10:   (7.5,8]    A    1
# 11:   (7.5,8]    B  700
# 12:   (8,8.5]    B  654
# 13:   (8,8.5]    C    2
# 14:   (8.5,9]    B  273
# 15:   (8.5,9]    C   70
# 16:   (9,9.5]    B   39
# 17:   (9,9.5]    C  383
# 18:  (9.5,10]    B    4
# 19:  (9.5,10]    C 1023
# 20: (10,10.5]    C 1065
# 21: (10,10.5]    D    6
# 22: (10.5,11]    C  406
# 23: (10.5,11]    D   92
# 24: (11,11.5]    C   49
# 25: (11,11.5]    D  543
# 26: (11.5,12]    C    2
# 27: (11.5,12]    D 1363
# 28: (12,12.5]    D 1334
# 29: (12.5,13]    D  561
# 30: (13,13.5]    D   92
# 31: (13.5,14]    D    8
# 32: (14,14.5]    D    1

2. Только по возрасту:

df <- setDT(input)[, .N, by = .(age_range = cut(age, range, include.lowest = TRUE))]
df[order(age_range),]
#    age_range    N
# 1:   (4.5,5]   20
# 2:   (5,5.5]  123
# 3:   (5.5,6]  337
# 4:   (6,6.5]  358
# 5:   (6.5,7]  198
# 6:   (7,7.5]  293
# 7:   (7.5,8]  701
# 8:   (8,8.5]  656
# 9:   (8.5,9]  343
# 10:   (9,9.5]  422
# 11:  (9.5,10] 1027
# 12: (10,10.5] 1071
# 13: (10.5,11]  498
# 14: (11,11.5]  592
# 15: (11.5,12] 1365
# 16: (12,12.5] 1334
# 17: (12.5,13]  561
# 18: (13,13.5]   92
# 19: (13.5,14]    8
# 20: (14,14.5]    1

Данные:

type <- c(rep("A",1000),        rep("B",2000),      rep("C",3000),      rep("D",4000))
age <-  c(rnorm(1000,6,0.5),    rnorm(2000,8,0.5),  rnorm(3000,10,0.5), rnorm(4000,12,0.5))
input <- data.frame(type,age,stringsAsFactors=FALSE)
range <- seq(floor(min(input$age)),ceiling(max(input$age)),0.5)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...