Announcement

Collapse
No announcement yet.
X
  • Filter
  • Time
  • Show
Clear All
new posts

  • How to calculate confidence intervals on LD50 after logit/probit

    Hi,
    In a theoretical experiment, I expose individual subjects to various doses of a toxic agent (example here is radiation), and later determine if the subject is dead or alive.
    I want to use either logit or probit to describe probability of death as a function of dose.
    Specifically, I want to calculate the dose that will kill 50% of the subjects (LD50), and I want to report LD50 with 95% CI.
    Finally, I want to test if the logit LD50 is different from the probit LD50.
    The last question doesn't make much sense, but if I understand how to do the test, I can use it on more interesting questions (like, if I only use logit and give a radio protector to some subjects, will they have a significantly higher LD50?).

    My questions are:
    a) How do I calculate SE on LD50, and use that to calculate 95% CI? I have seen this question raised in various forums and publications. It seems complicated, and I don't know which formula to use, or how to enter it in Stata in a way that refers only to stored results (like _b[cons]).
    b) What is the best reference for how to calculate SE?
    c) Have I made any other mistakes in the code below, including how to test the difference in LD50s and for the plot used for visual inspection of the data and the logit/probit functions?
    d) This is my first post, and please correct any mistakes I may have done on how to post a question on this list.
    Code:
    . version 14
    
    . webuse auto, clear                     // Generate data test set
    (1978 Automobile Data)
    
    . generate subjectID=_n                  // Clarify that observations are individual subjects
    
    . generate dose=(7500-weight)/100        // Exposure (radiation dose)
    
    . label variable dose "Radiation dose (Gy)"
    
    . generate survival=foreign              // Outcome (survival, 0 or 1)
    
    . label variable survival "Survival: 0=alive 1=dead"
    
    . drop w f m* p r h t* l di g
    
    . logit survival dose                    // logit
    
    Iteration 0:   log likelihood =  -45.03321  
    Iteration 1:   log likelihood = -30.669507  
    Iteration 2:   log likelihood = -29.068209  
    Iteration 3:   log likelihood = -29.054006  
    Iteration 4:   log likelihood = -29.054002  
    Iteration 5:   log likelihood = -29.054002  
    
    Logistic regression                             Number of obs     =         74
                                                    LR chi2(1)        =      31.96
                                                    Prob > chi2       =     0.0000
    Log likelihood = -29.054002                     Pseudo R2         =     0.3548
    
    ------------------------------------------------------------------------------
        survival |      Coef.   Std. Err.      z    P>|z|     [95% Conf. Interval]
    -------------+----------------------------------------------------------------
            dose |   .2587387   .0609368     4.25   0.000     .1393048    .3781727
           _cons |  -13.12281   3.018091    -4.35   0.000    -19.03816   -7.207457
    ------------------------------------------------------------------------------
    
    . scalar _consl   = _b[_cons]            // Used for plot
    
    . scalar dosel    = _b[dose]             // Used for plot
    
    . scalar LD50l    = -_b[_cons]/_b[dose]  // LD50 (logit)
    
    . *scalar LD50lse = ???                  // SE - Don't know how to do this
    . scalar LD50lse  = 5                    // Just a random number to run the next part
    
    . scalar LD50lupp = LD50l+(1.96*LD50lse) // Don't know if can be done on this scale
    
    . scalar LD50llow = LD50l-(1.96*LD50lse)
    
    . display "LD50(logit): " %4.2f LD50l " Gy"
    LD50(logit): 50.72 Gy
    
    . display "95% CI: " %4.2f LD50llow "-" %4.2f LD50lupp
    95% CI: 40.92-60.52
    
    . probit survival dose                   // probit
    
    Iteration 0:   log likelihood =  -45.03321  
    Iteration 1:   log likelihood = -29.534424  
    Iteration 2:   log likelihood = -28.912832  
    Iteration 3:   log likelihood = -28.908407  
    Iteration 4:   log likelihood = -28.908407  
    
    Probit regression                               Number of obs     =         74
                                                    LR chi2(1)        =      32.25
                                                    Prob > chi2       =     0.0000
    Log likelihood = -28.908407                     Pseudo R2         =     0.3581
    
    ------------------------------------------------------------------------------
        survival |      Coef.   Std. Err.      z    P>|z|     [95% Conf. Interval]
    -------------+----------------------------------------------------------------
            dose |     .15049   .0326453     4.61   0.000     .0865064    .2144735
           _cons |  -7.631124   1.602292    -4.76   0.000    -10.77156   -4.490688
    ------------------------------------------------------------------------------
    
    . scalar _consp   = _b[_cons]            // Used for plot
    
    . scalar dosep    = _b[dose]             // Used for plot
    
    . scalar LD50p    = -_b[_cons]/_b[dose]  // LD50 (probit)
    
    . *scalar LD50pse = ???                  // SE - Don't know how to do this
    . scalar LD50pse  = 5                    // Just a random number to run the next part
    
    . scalar LD50pupp = LD50p+(1.96*LD50pse) // Don't know if can be done on this scale
    
    . scalar LD50plow = LD50p-(1.96*LD50pse)
    
    . display "LD50(probit): " %4.2f LD50p " Gy"
    LD50(probit): 50.71 Gy
    
    . display "95% CI: " %4.2f LD50plow "-" %4.2f LD50pupp
    95% CI: 40.91-60.51
    
    . * Test difference between LD50logit and LD50probit
    . scalar Z    = (LD50l-LD50p)/sqrt(LD50lse^2+LD50pse^2)
    
    . scalar Pval = 2*normal(-abs(Z))
    
    . display "P value:" %9.2e Pval
    P value: 9.99e-01
    
    . * Plot data and logit/probit functions
    . twoway function y = invlogit(_consl + dosel*x), range(dose) ||      ///
    >        function y = normprob(_consp + dosep*x), range(dose) ||      ///
    >        scatter survival dose,                                       ///
    >        xtitle("Radiation dose (Gy)") ytitle("Probability of death") ///
    >        legend(order(1 2) label(1 "logit") label(2 "probit"))
    
    .
    Click image for larger version

Name:	LD50.png
Views:	1
Size:	10.9 KB
ID:	1294505

    Any help is greatly appreciated,
    Many thanks,
    Jan

  • #2
    The LD50 equation you gave holds for both logit and probit analyses. It is the value of dose for which:

    Code:
    _b[_cons]+_b[dose]*dose= 0
    or
    Code:
    dose = -_b[_cons]/_b[dose]
    You get the standard error via nlcom after logit, logistic, or probit.

    Code:
    nlcom  -_b[_cons]/_b[dose]
    Steve Samuels
    Statistical Consulting
    [email protected]

    Stata 14.2

    Comment


    • #3
      Here's how to compare the two LD50s, which was your last question. Look up in the Manual each command that is unfamiliar to you.

      Code:
      sysuse auto, clear
      rename foreign f
      
      logit f turn
      estimates store m1
      
      probit f turn
      estimates store m2
      
      suest m1 m2, vce(robust)
      
      nlcom (ratio1: -_b[m1_f:_cons]/_b[m1_f: turn]) ///
            (ratio2: -_b[m2_f:_cons]/_b[m2_f: turn]), post
      
      lincom _b[ratio1] -  _b[ratio2]
      Two qualifications:
      1) as most bioassays operate on log dose, you may want to modify the analysis above to operate on the log.
      2) The analysis assumes a single linear term in dose (or log dose). This assumption must be tested.
      Steve Samuels
      Statistical Consulting
      [email protected]

      Stata 14.2

      Comment


      • #4
        Just as a minor addendum to Steve's superlative answer: for evaluating a radioprotective agent, when you'd be using the same type of likelihood function for both treatment groups, you can model both treatment groups together in the same regression model using Stata's factor variables. This could be a slightly more powerful, albeit still asymptotic, approach than modeling separately and then combining the two estimates via seemingly unrelated estimation. (Although Steve's suest approach would be the only way that I'm aware of to answer the actual question that you did ask, viz., how compare probit to logit.) See illustration below using a toy example from the auto dataset.

        The factor-variable approach readily extends to more than two groups, for example, to examine a dose-response relationship for radioprotective agent in in its ability to elevate median lethal absorbed radiation dose.

        .ÿversionÿ14.0

        .ÿ
        .ÿclearÿ*

        .ÿsetÿmoreÿoff

        .ÿsetÿseedÿ`=date("2015-05-17",ÿ"YMD")'

        .ÿ
        .ÿsysuseÿauto
        (1978ÿAutomobileÿData)

        .ÿgenerateÿbyteÿgroupÿ=ÿruniform()ÿ>ÿ0.5

        .ÿsummarizeÿturn,ÿmeanonly

        .ÿquietlyÿreplaceÿturnÿ=ÿr(max)ÿ-ÿturn

        .ÿ
        .ÿ*
        .ÿ*ÿModelingÿcontrolÿandÿexperimentalÿtreatmentsÿtogether
        .ÿ*
        .ÿprobitÿforeignÿi.group##c.turn,ÿnolog

        ProbitÿregressionÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿNumberÿofÿobsÿÿÿÿÿ=ÿÿÿÿÿÿÿÿÿ74
        ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿLRÿchi2(3)ÿÿÿÿÿÿÿÿ=ÿÿÿÿÿÿ46.96
        ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿProbÿ>ÿchi2ÿÿÿÿÿÿÿ=ÿÿÿÿÿ0.0000
        Logÿlikelihoodÿ=ÿ-21.553929ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿPseudoÿR2ÿÿÿÿÿÿÿÿÿ=ÿÿÿÿÿ0.5214

        ------------------------------------------------------------------------------
        ÿÿÿÿÿforeignÿ|ÿÿÿÿÿÿCoef.ÿÿÿStd.ÿErr.ÿÿÿÿÿÿzÿÿÿÿP>|z|ÿÿÿÿÿ[95%ÿConf.ÿInterval]
        -------------+----------------------------------------------------------------
        ÿÿÿÿÿ1.groupÿ|ÿÿÿ2.315038ÿÿÿ2.167758ÿÿÿÿÿ1.07ÿÿÿ0.286ÿÿÿÿ-1.933688ÿÿÿÿ6.563765
        ÿÿÿÿÿÿÿÿturnÿ|ÿÿÿ.4684977ÿÿÿ.1257128ÿÿÿÿÿ3.73ÿÿÿ0.000ÿÿÿÿÿÿ.222105ÿÿÿÿ.7148903
        ÿÿÿÿÿÿÿÿÿÿÿÿÿ|
        group#c.turnÿ|
        ÿÿÿÿÿÿÿÿÿÿ1ÿÿ|ÿÿ-.2447664ÿÿÿ.1544472ÿÿÿÿ-1.58ÿÿÿ0.113ÿÿÿÿ-.5474773ÿÿÿÿ.0579445
        ÿÿÿÿÿÿÿÿÿÿÿÿÿ|
        ÿÿÿÿÿÿÿ_consÿ|ÿÿ-6.071586ÿÿÿ1.722725ÿÿÿÿ-3.52ÿÿÿ0.000ÿÿÿÿ-9.448065ÿÿÿ-2.695106
        ------------------------------------------------------------------------------
        Note:ÿ1ÿfailureÿandÿ0ÿsuccessesÿcompletelyÿdetermined.

        .ÿpredictÿdoubleÿy_hat
        (optionÿprÿassumed;ÿPr(foreign))

        .ÿ
        .ÿnlcomÿ(control:ÿÿ-_b[_cons]/_b[turn])ÿ///
        >ÿÿÿÿÿ(experimental:ÿ-(_b[_cons]ÿ+ÿ_b[1.group])ÿ/ÿ(_b[turn]ÿ+ÿ_b[1.group#c.turn])),ÿpost

        ÿÿÿÿÿcontrol:ÿÿ-_b[_cons]/_b[turn]
        experimental:ÿÿ-(_b[_cons]ÿ+ÿ_b[1.group])ÿ/ÿ(_b[turn]ÿ+ÿ_b[1.group#c.turn])

        ------------------------------------------------------------------------------
        ÿÿÿÿÿforeignÿ|ÿÿÿÿÿÿCoef.ÿÿÿStd.ÿErr.ÿÿÿÿÿÿzÿÿÿÿP>|z|ÿÿÿÿÿ[95%ÿConf.ÿInterval]
        -------------+----------------------------------------------------------------
        ÿÿÿÿÿcontrolÿ|ÿÿÿ12.95969ÿÿÿ.7960513ÿÿÿÿ16.28ÿÿÿ0.000ÿÿÿÿÿ11.39946ÿÿÿÿ14.51992
        experimentalÿ|ÿÿÿ16.79045ÿÿÿ1.538384ÿÿÿÿ10.91ÿÿÿ0.000ÿÿÿÿÿ13.77527ÿÿÿÿ19.80562
        ------------------------------------------------------------------------------

        .ÿlincomÿ_b[experimental]ÿ-ÿ_b[control]

        ÿ(ÿ1)ÿÿ-ÿcontrolÿ+ÿexperimentalÿ=ÿ0

        ------------------------------------------------------------------------------
        ÿÿÿÿÿforeignÿ|ÿÿÿÿÿÿCoef.ÿÿÿStd.ÿErr.ÿÿÿÿÿÿzÿÿÿÿP>|z|ÿÿÿÿÿ[95%ÿConf.ÿInterval]
        -------------+----------------------------------------------------------------
        ÿÿÿÿÿÿÿÿÿ(1)ÿ|ÿÿÿ3.830752ÿÿÿ1.732144ÿÿÿÿÿ2.21ÿÿÿ0.027ÿÿÿÿÿ.4358129ÿÿÿÿ7.225692
        ------------------------------------------------------------------------------

        .ÿ
        .ÿgraphÿtwowayÿlineÿy_hatÿturnÿifÿ!group,ÿsortÿlcolor(blue)ÿ///
        >ÿÿÿÿÿ||ÿscatterÿforeignÿturnÿifÿ!group,ÿmcolor(blue)ÿmsize(small)ÿ///
        >ÿÿÿÿÿ||ÿlineÿy_hatÿturnÿifÿgroup,ÿsortÿlcolor(red)ÿ///
        >ÿÿÿÿÿ||ÿscatterÿforeignÿturnÿifÿgroup,ÿmcolor(red)ÿmsize(small)ÿ///
        >ÿÿÿÿÿÿÿÿytitle(PredictedÿOutcomeÿ(%))ÿylabel(ÿ,ÿangle(horizontal)ÿnogrid)ÿ///
        >ÿÿÿÿÿÿÿÿÿÿÿÿxtitle(Whatever)ÿjitter(5)ÿlegend(off)ÿ///
        >ÿÿÿÿÿÿÿÿyline(0.5,ÿlcolor(black)ÿlpattern(dash))ÿ///
        >ÿÿÿÿÿÿÿÿxline(`=_b[control]',ÿlcolor(blue)ÿlpattern(dash))ÿ///
        >ÿÿÿÿÿÿÿÿxline(`=_b[experimental]',ÿlcolor(red)ÿlpattern(dash))ÿ///
        >ÿÿÿÿÿÿÿÿtext(0.45ÿ`=_b[control]'ÿ"Control",ÿplacement(right))ÿ///
        >ÿÿÿÿÿÿÿÿÿÿÿÿtext(0.45ÿ`=_b[experimental]'ÿ"Experimental",ÿplacement(right))

        .ÿquietlyÿgraphÿexportÿGraph.png,ÿreplace

        .ÿ
        .ÿ*
        .ÿ*ÿModelingÿseparatelyÿandÿcombiningÿwithÿseeminglyÿunrelatedÿestimation
        .ÿ*
        .ÿquietlyÿprobitÿforeignÿturnÿifÿ!group,ÿnolog

        .ÿestimatesÿstoreÿcontrol

        .ÿ
        .ÿquietlyÿprobitÿforeignÿturnÿifÿgroup,ÿnolog

        .ÿestimatesÿstoreÿexperimental

        .ÿ
        .ÿsuestÿcontrolÿexperimental

        Simultaneousÿresultsÿforÿcontrol,ÿexperimental

        ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿNumberÿofÿobsÿÿÿÿÿ=ÿÿÿÿÿÿÿÿÿ74

        --------------------------------------------------------------------------------------
        ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ|ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿRobust
        ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ|ÿÿÿÿÿÿCoef.ÿÿÿStd.ÿErr.ÿÿÿÿÿÿzÿÿÿÿP>|z|ÿÿÿÿÿ[95%ÿConf.ÿInterval]
        ---------------------+----------------------------------------------------------------
        control_foreignÿÿÿÿÿÿ|
        ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿturnÿ|ÿÿÿ.4684977ÿÿÿ.1348549ÿÿÿÿÿ3.47ÿÿÿ0.001ÿÿÿÿÿÿ.204187ÿÿÿÿ.7328084
        ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ_consÿ|ÿÿ-6.071586ÿÿÿ1.524133ÿÿÿÿ-3.98ÿÿÿ0.000ÿÿÿÿ-9.058832ÿÿÿÿ-3.08434
        ---------------------+----------------------------------------------------------------
        experimental_foreignÿ|
        ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿturnÿ|ÿÿÿ.2237312ÿÿÿ.0759031ÿÿÿÿÿ2.95ÿÿÿ0.003ÿÿÿÿÿÿ.074964ÿÿÿÿ.3724985
        ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ_consÿ|ÿÿ-3.756547ÿÿÿ.9521615ÿÿÿÿ-3.95ÿÿÿ0.000ÿÿÿÿÿ-5.62275ÿÿÿ-1.890345
        --------------------------------------------------------------------------------------

        .ÿnlcomÿ(ratio1:ÿ-_b[control_foreign:_cons]/_b[control_foreign:ÿturn])ÿ///
        >ÿÿÿÿÿ(ratio2:ÿ-_b[experimental_foreign:_cons]/_b[experimental_foreign:ÿturn]),ÿpost

        ÿÿÿÿÿÿratio1:ÿÿ-_b[control_foreign:_cons]/_b[control_foreign:ÿturn]
        ÿÿÿÿÿÿratio2:ÿÿ-_b[experimental_foreign:_cons]/_b[experimental_foreign:ÿturn]

        ------------------------------------------------------------------------------
        ÿÿÿÿÿÿÿÿÿÿÿÿÿ|ÿÿÿÿÿÿCoef.ÿÿÿStd.ÿErr.ÿÿÿÿÿÿzÿÿÿÿP>|z|ÿÿÿÿÿ[95%ÿConf.ÿInterval]
        -------------+----------------------------------------------------------------
        ÿÿÿÿÿÿratio1ÿ|ÿÿÿ12.95969ÿÿÿ.6990019ÿÿÿÿ18.54ÿÿÿ0.000ÿÿÿÿÿ11.58967ÿÿÿÿ14.32971
        ÿÿÿÿÿÿratio2ÿ|ÿÿÿ16.79045ÿÿÿ1.819641ÿÿÿÿÿ9.23ÿÿÿ0.000ÿÿÿÿÿ13.22401ÿÿÿÿ20.35688
        ------------------------------------------------------------------------------

        .ÿlincomÿ_b[ratio1]ÿ-ÿÿ_b[ratio2]

        ÿ(ÿ1)ÿÿratio1ÿ-ÿratio2ÿ=ÿ0

        ------------------------------------------------------------------------------
        ÿÿÿÿÿÿÿÿÿÿÿÿÿ|ÿÿÿÿÿÿCoef.ÿÿÿStd.ÿErr.ÿÿÿÿÿÿzÿÿÿÿP>|z|ÿÿÿÿÿ[95%ÿConf.ÿInterval]
        -------------+----------------------------------------------------------------
        ÿÿÿÿÿÿÿÿÿ(1)ÿ|ÿÿ-3.830752ÿÿÿ1.949281ÿÿÿÿ-1.97ÿÿÿ0.049ÿÿÿÿ-7.651273ÿÿÿ-.0102316
        ------------------------------------------------------------------------------

        .ÿ
        .ÿexit

        endÿofÿdo-file


        .


        Click image for larger version

Name:	Graph.png
Views:	1
Size:	17.6 KB
ID:	1294568

        Comment


        • #5
          Dear Steve and Joseph,
          Many, many thanks for your help, it was exactly what I needed.
          It will take me some time to go through it and try to understand the details, but it's so much easier now that I can work backwards from a functional code.
          Best wishes,
          Jan

          Comment

          Working...
          X