Search code examples
juliacox-regressionjulia-jump

Score Function of Cox proportional hazard in JuMP


I am not very experienced in converting equations to code. I got stuck on converting the score function of the partial likelihood to code in julia to be evaluated in JuMP.

The score function which, when solved for beta at 0, is the max.

I made a small simple data set.

Using DataFrames, DataFramesMeta, JuMP, Ipopt
#build DataFrame
times = [6,7,10,15,19,25]
is_censored = [1,0,1,1,0,1]
x= is_control = [1,1,0,1,0,0]

m = Model(solver=IpoptSolver(print_level=0))
using DataFrames

df = DataFrame();
df[:times]=times;
df[:is_censored]= is_censored;
df[:x]=x;
df

#sort df
df_sorted = sort!(df, cols = [order(:times)])

#make df_risk and df_uncensored
df_uncensored = @where(df_sorted, :is_censored .== 0)
df_risk = df_sorted

#use JuMP

##convert df to array

uncensored = convert(Array,df_uncensored[:x])
risk_set = convert(Array,df_risk[:x])
risk_index = convert(Array,find(is_censored .== 0))
x = convert(Array, x)
@variable(m, β, start = 0.0)

# score
@NLobjective(m, Max, sum(uncensored[i] - (([sum(exp(risk_set[j]*β)*x[j]) for j = risk_index[i]:length(risk_set)]) / ([(sum(exp(risk_set[j]*β)*x[j])) for j=risk_index[i]:length(risk_set)])) for i = 1:length(uncensored)))

The error I am getting is

ERROR: exp is not defined for type AffExpr. Are you trying to build a nonlinear problem? Make sure you use @NLconstraint/@NLobjective.
Stacktrace:
 [1] exp(::JuMP.GenericAffExpr{Float64,JuMP.Variable}) at /home/icarus/.julia/v0.6/JuMP/src/operators.jl:630
 [2] collect(::Base.Generator{UnitRange{Int64},##58#60}) at ./array.jl:418
 [3] macro expansion at /home/icarus/.julia/v0.6/JuMP/src/parseExpr_staged.jl:489 [inlined]
 [4] macro expansion at /home/icarus/.julia/v0.6/JuMP/src/parsenlp.jl:226 [inlined]
 [5] macro expansion at /home/icarus/.julia/v0.6/JuMP/src/macros.jl:1086 [inlined]
 [6] anonymous at ./<missing>:?

The error says the the exponential is the problem, but I previously did the log likelihood which has an exponential in it and got no errors.

In R the beta = -1.3261

If the above code was working I would expect that same output after running

solve(m)

println("β = ", getvalue(β))

Solution

  • The code in the question is a bit convoluted, but the following is an attempt to extract the relevant parts for a working method:

    using JuMP, Ipopt
    
    times = [6,7,10,15,19,25];
    is_censored = 1-[1,0,1,1,0,1];
    is_control = 1-[1,1,0,1,0,0];
    
    uncensored = find(is_censored .== 0)
    
    println("times = $times")
    println("is_censored = $is_censored")
    println("is_control = $is_control")
    
    m = Model(solver=IpoptSolver(print_level=0))
    @variable(m, β, start = 0.0)
    @NLobjective(m, Max, sum(log(1+(-1)^is_control[uncensored[i]]* 
      sum((-1)^is_control[j]*exp(is_control[j]*β) for j=uncensored[i]:length(times))/
      sum( exp(is_control[j]*β) for j=uncensored[i]:length(times)))
         for i=1:length(uncensored)))
    
    solve(m)
    println("β = ", getvalue(β))
    

    This outputs:

    times = [6,7,10,15,19,25]
    is_censored = [0,1,0,0,1,0]
    is_control = [0,0,1,0,1,1]
    
    β = -1.3261290591982942
    

    The β is the same one from the question, so I guess the tweaks to the input are correct and the formula is the log-likelihood. The beginning of the expression inside the log uses a common trick of choosing a +1 or -1 sign according to a 0/1 value bool with (-1)^bool.