lgb.Booster.R 17.6 KB
Newer Older
Guolin Ke's avatar
Guolin Ke committed
1
2
Booster <- R6Class(
  "lgb.Booster",
3
  cloneable = FALSE,
Guolin Ke's avatar
Guolin Ke committed
4
  public = list(
5
    best_iter    = -1,
Guolin Ke's avatar
Guolin Ke committed
6
    record_evals = list(),
7
8
9
    finalize     = function() {
      if (!lgb.is.null.handle(private$handle)) {
        lgb.call("LGBM_BoosterFree_R", ret = NULL, private$handle)
Guolin Ke's avatar
Guolin Ke committed
10
11
        private$handle <- NULL
      }
12
13
    },
    initialize = function(params    = list(),
Guolin Ke's avatar
Guolin Ke committed
14
15
16
                          train_set = NULL,
                          modelfile = NULL,
                          ...) {
17
      params     <- append(params, list(...))
Guolin Ke's avatar
Guolin Ke committed
18
      params_str <- lgb.params2str(params)
19
      handle     <- lgb.new.handle()
Guolin Ke's avatar
Guolin Ke committed
20
21
      if (!is.null(train_set)) {
        if (!lgb.check.r6.class(train_set, "lgb.Dataset")) {
22
          stop("lgb.Booster: Can only use lgb.Dataset as training data")
Guolin Ke's avatar
Guolin Ke committed
23
        }
24

Guolin Ke's avatar
Guolin Ke committed
25
        handle <-
26
27
28
29
          lgb.call("LGBM_BoosterCreate_R", ret = handle, train_set$.__enclos_env__$private$get_handle(), params_str)

        private$train_set      <- train_set
        private$num_dataset    <- 1
Guolin Ke's avatar
Guolin Ke committed
30
31
        private$init_predictor <- train_set$.__enclos_env__$private$predictor
        if (!is.null(private$init_predictor)) {
32
          lgb.call("LGBM_BoosterMerge_R", ret = NULL,
Guolin Ke's avatar
Guolin Ke committed
33
34
35
                handle,
                private$init_predictor$.__enclos_env__$private$handle)
        }
36
        private$is_predicted_cur_iter <- c(private$is_predicted_cur_iter, FALSE)
Guolin Ke's avatar
Guolin Ke committed
37
38
      } else if (!is.null(modelfile)) {
        if (!is.character(modelfile)) {
39
          stop("lgb.Booster: Can only use a string as model file path")
Guolin Ke's avatar
Guolin Ke committed
40
41
42
        }
        handle <-
          lgb.call("LGBM_BoosterCreateFromModelfile_R",
43
            ret = handle,
Guolin Ke's avatar
Guolin Ke committed
44
45
46
            lgb.c_str(modelfile))
      } else {
        stop(
47
          "lgb.Booster: Need at least either training dataset or model file to create booster instance"
Guolin Ke's avatar
Guolin Ke committed
48
49
        )
      }
50
51
      class(handle)     <- "lgb.Booster.handle"
      private$handle    <- handle
52
      private$num_class <- 1L
Guolin Ke's avatar
Guolin Ke committed
53
      private$num_class <-
54
        lgb.call("LGBM_BoosterGetNumClasses_R", ret = private$num_class, private$handle)
Guolin Ke's avatar
Guolin Ke committed
55
56
57
    },
    set_train_data_name = function(name) {
      private$name_train_set <- name
58
      self
Guolin Ke's avatar
Guolin Ke committed
59
60
61
    },
    add_valid = function(data, name) {
      if (!lgb.check.r6.class(data, "lgb.Dataset")) {
62
        stop("lgb.Booster.add_valid: Can only use lgb.Dataset as validation data")
Guolin Ke's avatar
Guolin Ke committed
63
64
65
      }
      if (!identical(data$.__enclos_env__$private$predictor, private$init_predictor)) {
        stop(
66
          "lgb.Booster.add_valid: Failed to add validation data; you should use the same predictor for these data"
Guolin Ke's avatar
Guolin Ke committed
67
68
        )
      }
69
70
      if (!is.character(name)) {
        stop("lgb.Booster.add_valid: Can only use characters as data name")
Guolin Ke's avatar
Guolin Ke committed
71
      }
72
73
74
75
      lgb.call("LGBM_BoosterAddValidData_R", ret = NULL, private$handle, data$.__enclos_env__$private$get_handle())
      private$valid_sets            <- c(private$valid_sets, data)
      private$name_valid_sets       <- c(private$name_valid_sets, name)
      private$num_dataset           <- private$num_dataset + 1
Guolin Ke's avatar
Guolin Ke committed
76
77
      private$is_predicted_cur_iter <-
        c(private$is_predicted_cur_iter, FALSE)
78
79

      self
Guolin Ke's avatar
Guolin Ke committed
80
81
    },
    reset_parameter = function(params, ...) {
82
      params     <- append(params, list(...))
Guolin Ke's avatar
Guolin Ke committed
83
      params_str <- algb.params2str(params)
84
      lgb.call("LGBM_BoosterResetParameter_R", ret = NULL,
Guolin Ke's avatar
Guolin Ke committed
85
86
            private$handle,
            params_str)
87
      self
Guolin Ke's avatar
Guolin Ke committed
88
89
90
91
92
93
94
95
    },
    update = function(train_set = NULL, fobj = NULL) {
      if (!is.null(train_set)) {
        if (!lgb.check.r6.class(train_set, "lgb.Dataset")) {
          stop("lgb.Booster.update: Only can use lgb.Dataset as training data")
        }
        if (!identical(train_set$predictor, private$init_predictor)) {
          stop(
96
            "lgb.Booster.update: Change train_set failed, you should use the same predictor for these data"
Guolin Ke's avatar
Guolin Ke committed
97
98
          )
        }
99
        lgb.call("LGBM_BoosterResetTrainingData_R", ret = NULL,
Guolin Ke's avatar
Guolin Ke committed
100
101
102
103
104
              private$handle,
              train_set$.__enclos_env__$private$get_handle())
        private$train_set = train_set
      }
      if (is.null(fobj)) {
105
        ret <- lgb.call("LGBM_BoosterUpdateOneIter_R", ret = NULL, private$handle)
Guolin Ke's avatar
Guolin Ke committed
106
      } else {
107
        if (!is.function(fobj)) { stop("lgb.Booster.update: fobj should be a function") }
Guolin Ke's avatar
Guolin Ke committed
108
        gpair <- fobj(private$inner_predict(1), private$train_set)
109
110
111
112
        if(is.null(gpair$grad) | is.null(gpair$hess)){
          stop("lgb.Booster.update: custom objective should 
            return a list with attributes (hess, grad)")
        }
113
114
        ret   <- lgb.call(
            "LGBM_BoosterUpdateOneIterCustom_R", ret = NULL,
Guolin Ke's avatar
Guolin Ke committed
115
116
117
118
119
120
            private$handle,
            gpair$grad,
            gpair$hess,
            length(gpair$grad)
          )
      }
121
      for (i in seq_along(private$is_predicted_cur_iter)) {
Guolin Ke's avatar
Guolin Ke committed
122
123
        private$is_predicted_cur_iter[[i]] <- FALSE
      }
124
      ret
Guolin Ke's avatar
Guolin Ke committed
125
126
    },
    rollback_one_iter = function() {
127
128
      lgb.call("LGBM_BoosterRollbackOneIter_R", ret = NULL, private$handle)
      for (i in seq_along(private$is_predicted_cur_iter)) {
Guolin Ke's avatar
Guolin Ke committed
129
130
        private$is_predicted_cur_iter[[i]] <- FALSE
      }
131
      self
Guolin Ke's avatar
Guolin Ke committed
132
133
    },
    current_iter = function() {
134
      cur_iter <- 0L
135
      lgb.call("LGBM_BoosterGetCurrentIteration_R", ret = cur_iter, private$handle)
Guolin Ke's avatar
Guolin Ke committed
136
137
138
    },
    eval = function(data, name, feval = NULL) {
      if (!lgb.check.r6.class(data, "lgb.Dataset")) {
139
        stop("lgb.Booster.eval: Can only use lgb.Dataset to eval")
Guolin Ke's avatar
Guolin Ke committed
140
141
      }
      data_idx <- 0
142
143
144
      if (identical(data, private$train_set)) { data_idx <- 1 } else {
        if (length(private$valid_sets) > 0) {
          for (i in seq_along(private$valid_sets)) {
Guolin Ke's avatar
Guolin Ke committed
145
146
147
148
149
150
151
152
153
154
155
            if (identical(data, private$valid_sets[[i]])) {
              data_idx <- i + 1
              break
            }
          }
        }
      }
      if (data_idx == 0) {
        self$add_valid(data, name)
        data_idx <- private$num_dataset
      }
156
      private$inner_eval(name, data_idx, feval)
Guolin Ke's avatar
Guolin Ke committed
157
158
    },
    eval_train = function(feval = NULL) {
159
      private$inner_eval(private$name_train_set, 1, feval)
Guolin Ke's avatar
Guolin Ke committed
160
161
162
    },
    eval_valid = function(feval = NULL) {
      ret = list()
163
164
165
      if (length(private$valid_sets) <= 0) { return(ret) }
      for (i in seq_along(private$valid_sets)) {
        ret <- append(ret, private$inner_eval(private$name_valid_sets[[i]], i + 1, feval))
Guolin Ke's avatar
Guolin Ke committed
166
      }
167
      ret
Guolin Ke's avatar
Guolin Ke committed
168
169
    },
    save_model = function(filename, num_iteration = NULL) {
170
      if (is.null(num_iteration)) { num_iteration <- self$best_iter }
Guolin Ke's avatar
Guolin Ke committed
171
172
173
174
175
176
177
      lgb.call(
        "LGBM_BoosterSaveModel_R",
        ret = NULL,
        private$handle,
        as.integer(num_iteration),
        lgb.c_str(filename)
      )
178
      self
Guolin Ke's avatar
Guolin Ke committed
179
180
    },
    dump_model = function(num_iteration = NULL) {
181
182
183
184
185
      if (is.null(num_iteration)) { num_iteration <- self$best_iter }
      lgb.call.return.str(
        "LGBM_BoosterDumpModel_R",
        private$handle,
        as.integer(num_iteration)
Guolin Ke's avatar
Guolin Ke committed
186
187
188
      )
    },
    predict = function(data,
189
190
191
192
193
194
                      num_iteration = NULL,
                      rawscore      = FALSE,
                      predleaf      = FALSE,
                      header        = FALSE,
                      reshape       = FALSE) {
      if (is.null(num_iteration)) { num_iteration <- self$best_iter }
Guolin Ke's avatar
Guolin Ke committed
195
      predictor <- Predictor$new(private$handle)
196
      predictor$predict(data, num_iteration, rawscore, predleaf, header, reshape)
Guolin Ke's avatar
Guolin Ke committed
197
    },
198
199
200
201
202
203
204
205
    to_predictor = function() { Predictor$new(private$handle) },
    raw = NA,
    save = function() {
      temp <- tempfile()
      lgb.save(self, temp)
      self$raw <- readChar(temp, file.info(temp)$size)
      file.remove(temp)
    }
Guolin Ke's avatar
Guolin Ke committed
206
207
  ),
  private = list(
208
209
210
211
212
213
214
215
216
217
218
    handle                   = NULL,
    train_set                = NULL,
    name_train_set           = "training",
    valid_sets               = list(),
    name_valid_sets          = list(),
    predict_buffer           = list(),
    is_predicted_cur_iter    = list(),
    num_class                = 1,
    num_dataset              = 0,
    init_predictor           = NULL,
    eval_names               = NULL,
Guolin Ke's avatar
Guolin Ke committed
219
    higher_better_inner_eval = NULL,
220
    inner_predict            = function(idx) {
Guolin Ke's avatar
Guolin Ke committed
221
      data_name <- private$name_train_set
222
      if (idx > 1) { data_name <- private$name_valid_sets[[idx - 1]] }
Guolin Ke's avatar
Guolin Ke committed
223
224
225
226
      if (idx > private$num_dataset) {
        stop("data_idx should not be greater than num_dataset")
      }
      if (is.null(private$predict_buffer[[data_name]])) {
227
        npred <- 0L
228
        npred <- lgb.call("LGBM_BoosterGetNumPredict_R",
Guolin Ke's avatar
Guolin Ke committed
229
230
231
232
233
234
                ret = npred,
                private$handle,
                as.integer(idx - 1))
        private$predict_buffer[[data_name]] <- rep(0.0, npred)
      }
      if (!private$is_predicted_cur_iter[[idx]]) {
235
        private$predict_buffer[[data_name]] <- lgb.call(
Guolin Ke's avatar
Guolin Ke committed
236
            "LGBM_BoosterGetPredict_R",
237
            ret = private$predict_buffer[[data_name]],
Guolin Ke's avatar
Guolin Ke committed
238
239
240
241
242
            private$handle,
            as.integer(idx - 1)
          )
        private$is_predicted_cur_iter[[idx]] <- TRUE
      }
243
      private$predict_buffer[[data_name]]
Guolin Ke's avatar
Guolin Ke committed
244
245
246
    },
    get_eval_info = function() {
      if (is.null(private$eval_names)) {
247
248
        names <- lgb.call.return.str("LGBM_BoosterGetEvalNames_R", private$handle)
        if (nchar(names) > 0) {
Guolin Ke's avatar
Guolin Ke committed
249
250
          names <- strsplit(names, "\t")[[1]]
          private$eval_names <- names
251
252
          private$higher_better_inner_eval <- rep(FALSE, length(names))
          for (i in seq_along(names)) {
253
            if ((names[i] == "auc") | grepl("^ndcg", names[i])) {
Guolin Ke's avatar
Guolin Ke committed
254
255
256
257
258
              private$higher_better_inner_eval[i] <- TRUE
            }
          }
        }
      }
259
      private$eval_names
Guolin Ke's avatar
Guolin Ke committed
260
261
262
263
264
265
266
267
268
    },
    inner_eval = function(data_name, data_idx, feval = NULL) {
      if (data_idx > private$num_dataset) {
        stop("data_idx should not be greater than num_dataset")
      }
      private$get_eval_info()
      ret <- list()
      if (length(private$eval_names) > 0) {
        tmp_vals <- rep(0.0, length(private$eval_names))
269
        tmp_vals <- lgb.call("LGBM_BoosterGetEval_R", ret = tmp_vals,
Guolin Ke's avatar
Guolin Ke committed
270
271
                private$handle,
                as.integer(data_idx - 1))
272
273
274
275
276
        for (i in seq_along(private$eval_names)) {
          res               <- list()
          res$data_name     <- data_name
          res$name          <- private$eval_names[i]
          res$value         <- tmp_vals[i]
Guolin Ke's avatar
Guolin Ke committed
277
          res$higher_better <- private$higher_better_inner_eval[i]
278
          ret               <- append(ret, list(res))
Guolin Ke's avatar
Guolin Ke committed
279
280
281
        }
      }
      if (!is.null(feval)) {
282
        if (!is.function(feval)) {
Guolin Ke's avatar
Guolin Ke committed
283
284
285
          stop("lgb.Booster.eval: feval should be a function")
        }
        data <- private$train_set
286
        if (data_idx > 1) { data <- private$valid_sets[[data_idx - 1]] }
287
288
289
290
291
292
        res <- feval(private$inner_predict(data_idx), data)
        if(is.null(res$name) | is.null(res$value) | 
           is.null(res$higher_better)) {
          stop("lgb.Booster.eval: custom eval function should return a 
            list with attribute (name, value, higher_better)");
        }
Guolin Ke's avatar
Guolin Ke committed
293
        res$data_name <- data_name
294
        ret           <- append(ret, list(res))
Guolin Ke's avatar
Guolin Ke committed
295
      }
296
      ret
Guolin Ke's avatar
Guolin Ke committed
297
298
299
300
301
302
    }
  )
)


#' Predict method for LightGBM model
303
#'
Guolin Ke's avatar
Guolin Ke committed
304
#' Predicted values based on class \code{lgb.Booster}
305
#'
Guolin Ke's avatar
Guolin Ke committed
306
307
308
#' @param object Object of class \code{lgb.Booster}
#' @param data a \code{matrix} object, a \code{dgCMatrix} object or a character representing a filename
#' @param num_iteration number of iteration want to predict with, NULL or <= 0 means use best iteration
309
310
#' @param rawscore whether the prediction should be returned in the for of original untransformed
#'        sum of predictions from boosting iterations' results. E.g., setting \code{rawscore=TRUE} for
Guolin Ke's avatar
Guolin Ke committed
311
#'        logistic regression would result in predictions for log-odds instead of probabilities.
312
#' @param predleaf whether predict leaf index instead.
Guolin Ke's avatar
Guolin Ke committed
313
#' @param header only used for prediction for text file. True if text file has header
314
315
#' @param reshape whether to reshape the vector of predictions to a matrix form when there are several
#'        prediction outputs per case.
Guolin Ke's avatar
Guolin Ke committed
316

317
#' @return
Guolin Ke's avatar
Guolin Ke committed
318
#' For regression or binary classification, it returns a vector of length \code{nrows(data)}.
319
320
#' For multiclass classification, either a \code{num_class * nrows(data)} vector or
#' a \code{(nrows(data), num_class)} dimension matrix is returned, depending on
Guolin Ke's avatar
Guolin Ke committed
321
#' the \code{reshape} value.
322
323
#'
#' When \code{predleaf = TRUE}, the output is a matrix object with the
Guolin Ke's avatar
Guolin Ke committed
324
325
#' number of columns corresponding to the number of trees.
#' @examples
326
327
328
329
330
331
332
333
334
335
336
337
338
#' \dontrun{
#'   library(lightgbm)
#'   data(agaricus.train, package='lightgbm')
#'   train <- agaricus.train
#'   dtrain <- lgb.Dataset(train$data, label=train$label)
#'   data(agaricus.test, package='lightgbm')
#'   test <- agaricus.test
#'   dtest <- lgb.Dataset.create.valid(dtrain, test$data, label=test$label)
#'   params <- list(objective="regression", metric="l2")
#'   valids <- list(test=dtest)
#'   model <- lgb.train(params, dtrain, 100, valids, min_data=1, learning_rate=1, early_stopping_rounds=10)
#'   preds <- predict(model, test$data)
#' }
Guolin Ke's avatar
Guolin Ke committed
339
340
#' @rdname predict.lgb.Booster
#' @export
341
predict.lgb.Booster <- function(object, data,
Guolin Ke's avatar
Guolin Ke committed
342
                        num_iteration = NULL,
343
344
345
346
347
348
                        rawscore      = FALSE,
                        predleaf      = FALSE,
                        header        = FALSE,
                        reshape       = FALSE) {
  if (!lgb.is.Booster(object)) {
    stop("predict.lgb.Booster: object should be an ", sQuote("lgb.Booster"))
Guolin Ke's avatar
Guolin Ke committed
349
350
351
352
353
  }
  object$predict(data, num_iteration, rawscore, predleaf, header, reshape)
}

#' Load LightGBM model
354
#'
Guolin Ke's avatar
Guolin Ke committed
355
#' Load LightGBM model from saved model file
356
#'
Guolin Ke's avatar
Guolin Ke committed
357
#' @param filename path of model file
358
#'
Guolin Ke's avatar
Guolin Ke committed
359
360
#' @return booster
#' @examples
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
#' \dontrun{
#'   library(lightgbm)
#'   data(agaricus.train, package='lightgbm')
#'   train <- agaricus.train
#'   dtrain <- lgb.Dataset(train$data, label=train$label)
#'   data(agaricus.test, package='lightgbm')
#'   test <- agaricus.test
#'   dtest <- lgb.Dataset.create.valid(dtrain, test$data, label=test$label)
#'   params <- list(objective="regression", metric="l2")
#'   valids <- list(test=dtest)
#'   model <- lgb.train(params, dtrain, 100, valids, min_data=1, learning_rate=1, early_stopping_rounds=10)
#'   lgb.save(model, "model.txt")
#'   load_booster <- lgb.load("model.txt")
#' }
#' @rdname lgb.load
Guolin Ke's avatar
Guolin Ke committed
376
377
#' @export
lgb.load <- function(filename){
378
379
  if (!is.character(filename)) { stop("lgb.load: filename should be character") }
  Booster$new(modelfile = filename)
Guolin Ke's avatar
Guolin Ke committed
380
381
382
}

#' Save LightGBM model
383
#'
Guolin Ke's avatar
Guolin Ke committed
384
#' Save LightGBM model
385
#'
Guolin Ke's avatar
Guolin Ke committed
386
387
388
#' @param booster Object of class \code{lgb.Booster}
#' @param filename saved filename
#' @param num_iteration number of iteration want to predict with, NULL or <= 0 means use best iteration
389
#'
Guolin Ke's avatar
Guolin Ke committed
390
391
#' @return booster
#' @examples
392
393
394
395
396
397
398
399
400
401
402
403
404
405
#' \dontrun{
#'   library(lightgbm)
#'   data(agaricus.train, package='lightgbm')
#'   train <- agaricus.train
#'   dtrain <- lgb.Dataset(train$data, label=train$label)
#'   data(agaricus.test, package='lightgbm')
#'   test <- agaricus.test
#'   dtest <- lgb.Dataset.create.valid(dtrain, test$data, label=test$label)
#'   params <- list(objective="regression", metric="l2")
#'   valids <- list(test=dtest)
#'   model <- lgb.train(params, dtrain, 100, valids, min_data=1, learning_rate=1, early_stopping_rounds=10)
#'   lgb.save(model, "model.txt")
#' }
#' @rdname lgb.save
Guolin Ke's avatar
Guolin Ke committed
406
#' @export
407
408
409
lgb.save <- function(booster, filename, num_iteration = NULL){
  if (!lgb.is.Booster(booster)) { stop("lgb.save: booster should be an ", sQuote("lgb.Booster")) }
  if (!is.character(filename)) { stop("lgb.save: filename should be a character") }
Guolin Ke's avatar
Guolin Ke committed
410
411
412
413
  booster$save_model(filename, num_iteration)
}

#' Dump LightGBM model to json
414
#'
Guolin Ke's avatar
Guolin Ke committed
415
#' Dump LightGBM model to json
416
#'
Guolin Ke's avatar
Guolin Ke committed
417
418
#' @param booster Object of class \code{lgb.Booster}
#' @param num_iteration number of iteration want to predict with, NULL or <= 0 means use best iteration
419
#'
Guolin Ke's avatar
Guolin Ke committed
420
421
#' @return json format of model
#' @examples
422
423
424
425
426
427
428
429
430
431
432
433
434
435
#' \dontrun{
#'   library(lightgbm)
#'   data(agaricus.train, package='lightgbm')
#'   train <- agaricus.train
#'   dtrain <- lgb.Dataset(train$data, label=train$label)
#'   data(agaricus.test, package='lightgbm')
#'   test <- agaricus.test
#'   dtest <- lgb.Dataset.create.valid(dtrain, test$data, label=test$label)
#'   params <- list(objective="regression", metric="l2")
#'   valids <- list(test=dtest)
#'   model <- lgb.train(params, dtrain, 100, valids, min_data=1, learning_rate=1, early_stopping_rounds=10)
#'   json_model <- lgb.dump(model)
#' }
#' @rdname lgb.dump
Guolin Ke's avatar
Guolin Ke committed
436
#' @export
437
438
lgb.dump <- function(booster, num_iteration = NULL){
  if (!lgb.is.Booster(booster)) { stop("lgb.save: booster should be an ", sQuote("lgb.Booster")) }
Guolin Ke's avatar
Guolin Ke committed
439
440
441
442
  booster$dump_model(num_iteration)
}

#' Get record evaluation result from booster
443
#'
Guolin Ke's avatar
Guolin Ke committed
444
445
446
447
448
449
450
451
452
#' Get record evaluation result from booster
#' @param booster Object of class \code{lgb.Booster}
#' @param data_name name of dataset
#' @param eval_name name of evaluation
#' @param iters iterations, NULL will return all
#' @param is_err TRUE will return evaluation error instead
#' @return vector of evaluation result
#' @rdname lgb.get.eval.result
#' @export
453
454
455
lgb.get.eval.result <- function(booster, data_name, eval_name, iters = NULL, is_err = FALSE) {
  if (!lgb.is.Booster(booster)) {
    stop("lgb.get.eval.result: Can only use ", sQuote("lgb.Booster"), " to get eval result")
Guolin Ke's avatar
Guolin Ke committed
456
  }
457
458
  if (!is.character(data_name) || !is.character(eval_name)) {
    stop("lgb.get.eval.result: data_name and eval_name should be characters")
Guolin Ke's avatar
Guolin Ke committed
459
  }
460
  if (is.null(booster$record_evals[[data_name]])) {
Guolin Ke's avatar
Guolin Ke committed
461
462
    stop("lgb.get.eval.result: wrong data name")
  }
463
  if (is.null(booster$record_evals[[data_name]][[eval_name]])) {
Guolin Ke's avatar
Guolin Ke committed
464
465
466
    stop("lgb.get.eval.result: wrong eval name")
  }
  result <- booster$record_evals[[data_name]][[eval_name]]$eval
467
  if (is_err) {
Guolin Ke's avatar
Guolin Ke committed
468
469
    result <- booster$record_evals[[data_name]][[eval_name]]$eval_err
  }
470
  if (is.null(iters)) {
Guolin Ke's avatar
Guolin Ke committed
471
472
473
474
475
    return(as.numeric(result))
  }
  iters <- as.integer(iters)
  delta <- booster$record_evals$start_iter - 1
  iters <- iters - delta
476
  as.numeric(result[iters])
Guolin Ke's avatar
Guolin Ke committed
477
}