utils.R 7.71 KB
Newer Older
1
2
3
lgb.is.Booster <- function(x) {
  lgb.check.r6.class(x, "lgb.Booster") # Checking if it is of class lgb.Booster or not
}
4

5
6
7
lgb.is.Dataset <- function(x) {
  lgb.check.r6.class(x, "lgb.Dataset") # Checking if it is of class lgb.Dataset or not
}
8

9
lgb.is.null.handle <- function(x) {
10
  is.null(x) || is.na(x)
11
}
Guolin Ke's avatar
Guolin Ke committed
12
13

lgb.encode.char <- function(arr, len) {
14
  if (!is.raw(arr)) {
15
    stop("lgb.encode.char: Can only encode from raw type")
Guolin Ke's avatar
Guolin Ke committed
16
  }
17
  return(rawToChar(arr[seq_len(len)]))
Guolin Ke's avatar
Guolin Ke committed
18
19
}

20
21
# [description] Raise an error. Before raising that error, check for any error message
#               stored in a buffer on the C++ side.
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
lgb.last_error <- function() {
  # Perform text error buffering
  buf_len <- 200L
  act_len <- 0L
  err_msg <- raw(buf_len)
  err_msg <- .Call(
    "LGBM_GetLastError_R"
    , buf_len
    , act_len
    , err_msg
    , PACKAGE = "lib_lightgbm"
  )

  # Check error buffer
  if (act_len > buf_len) {
    buf_len <- act_len
    err_msg <- raw(buf_len)
    err_msg <- .Call(
      "LGBM_GetLastError_R"
      , buf_len
      , act_len
      , err_msg
      , PACKAGE = "lib_lightgbm"
    )
  }

  # Return error
  stop("api error: ", lgb.encode.char(err_msg, act_len))
}

Guolin Ke's avatar
Guolin Ke committed
52
lgb.call <- function(fun_name, ret, ...) {
53
  # Set call state to a zero value
54
  call_state <- 0L
55

56
  # Check for a ret call
Guolin Ke's avatar
Guolin Ke committed
57
  if (!is.null(ret)) {
58
59
60
61
62
63
64
    call_state <- .Call(
      fun_name
      , ...
      , ret
      , call_state
      , PACKAGE = "lib_lightgbm"
    )
Guolin Ke's avatar
Guolin Ke committed
65
  } else {
66
67
68
69
70
71
    call_state <- .Call(
      fun_name
      , ...
      , call_state
      , PACKAGE = "lib_lightgbm"
    )
Guolin Ke's avatar
Guolin Ke committed
72
  }
Guolin Ke's avatar
Guolin Ke committed
73
  call_state <- as.integer(call_state)
74
  # Check for call state value post call
75
  if (call_state != 0L) {
76
    lgb.last_error()
Guolin Ke's avatar
Guolin Ke committed
77
  }
Guolin Ke's avatar
Guolin Ke committed
78

79
  return(ret)
80

Guolin Ke's avatar
Guolin Ke committed
81
82
83
}

lgb.call.return.str <- function(fun_name, ...) {
84

85
  # Create buffer
86
  buf_len <- as.integer(1024L * 1024L)
87
  act_len <- 0L
Guolin Ke's avatar
Guolin Ke committed
88
  buf <- raw(buf_len)
89

90
  # Call buffer
Guolin Ke's avatar
Guolin Ke committed
91
  buf <- lgb.call(fun_name, ret = buf, ..., buf_len, act_len)
92

93
  # Check for buffer content
Guolin Ke's avatar
Guolin Ke committed
94
95
  if (act_len > buf_len) {
    buf_len <- act_len
96
97
    buf <- raw(buf_len)
    buf <- lgb.call(fun_name, ret = buf, ..., buf_len, act_len)
Guolin Ke's avatar
Guolin Ke committed
98
  }
99

100
101
  # Return encoded character
  return(lgb.encode.char(buf, act_len))
102

Guolin Ke's avatar
Guolin Ke committed
103
104
105
}

lgb.params2str <- function(params, ...) {
106

107
  # Check for a list as input
108
  if (!identical(class(params), "list")) {
109
110
    stop("params must be a list")
  }
111

112
  # Split parameter names
Guolin Ke's avatar
Guolin Ke committed
113
  names(params) <- gsub("\\.", "_", names(params))
114

115
  # Merge parameters from the params and the dots-expansion
Guolin Ke's avatar
Guolin Ke committed
116
117
  dot_params <- list(...)
  names(dot_params) <- gsub("\\.", "_", names(dot_params))
118

119
  # Check for identical parameters
120
  if (length(intersect(names(params), names(dot_params))) > 0L) {
121
122
123
124
125
126
127
    stop(
      "Same parameters in "
      , sQuote("params")
      , " and in the call are not allowed. Please check your "
      , sQuote("params")
      , " list"
    )
128
  }
129

130
  # Merge parameters
Guolin Ke's avatar
Guolin Ke committed
131
  params <- c(params, dot_params)
132

133
134
  # Setup temporary variable
  ret <- list()
135

136
  # Perform key value join
Guolin Ke's avatar
Guolin Ke committed
137
  for (key in names(params)) {
138

139
140
141
142
143
144
145
146
147
148
149
    # If a parameter has multiple values, join those values together with commas.
    # trimws() is necessary because format() will pad to make strings the same width
    val <- paste0(
      trimws(
        format(
          x = params[[key]]
          , scientific = FALSE
        )
      )
      , collapse = ","
    )
150
    if (nchar(val) <= 0L) next # Skip join
151

152
    # Join key value
Guolin Ke's avatar
Guolin Ke committed
153
    pair <- paste0(c(key, val), collapse = "=")
154
    ret <- c(ret, pair)
155

Guolin Ke's avatar
Guolin Ke committed
156
  }
157

158
  # Check ret length
159
  if (length(ret) == 0L) {
160
    return(lgb.c_str(""))
Guolin Ke's avatar
Guolin Ke committed
161
  }
162

163
164
165
  # Return string separated by a space per element
  return(lgb.c_str(paste0(ret, collapse = " ")))

Guolin Ke's avatar
Guolin Ke committed
166
167
}

168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
lgb.check_interaction_constraints <- function(params, column_names) {

  # Convert interaction constraints to feature numbers
  string_constraints <- list()

  if (!is.null(params[["interaction_constraints"]])) {

    # validation
    if (!methods::is(params[["interaction_constraints"]], "list")) {
        stop("interaction_constraints must be a list")
    }
    if (!all(sapply(params[["interaction_constraints"]], function(x) {is.character(x) || is.numeric(x)}))) {
        stop("every element in interaction_constraints must be a character vector or numeric vector")
    }

    for (constraint in params[["interaction_constraints"]]) {

      # Check for character name
      if (is.character(constraint)) {

          constraint_indices <- as.integer(match(constraint, column_names) - 1L)

          # Provided indices, but some indices are not existing?
          if (sum(is.na(constraint_indices)) > 0L) {
            stop(
              "supplied an unknown feature in interaction_constraints "
              , sQuote(constraint[is.na(constraint_indices)])
            )
          }

        } else {

          # Check that constraint indices are at most number of features
          if (max(constraint) > length(column_names)) {
            stop(
              "supplied a too large value in interaction_constraints: "
              , max(constraint)
              , " but only "
              , length(column_names)
              , " features"
            )
          }

          # Store indices as [0, n-1] indexed instead of [1, n] indexed
          constraint_indices <- as.integer(constraint - 1L)

        }

        # Convert constraint to string
        constraint_string <- paste0("[", paste0(constraint_indices, collapse = ","), "]")
        string_constraints <- append(string_constraints, constraint_string)
    }

  }

  return(string_constraints)

}

Guolin Ke's avatar
Guolin Ke committed
227
lgb.c_str <- function(x) {
228

229
  # Perform character to raw conversion
Guolin Ke's avatar
Guolin Ke committed
230
  ret <- charToRaw(as.character(x))
231
  ret <- c(ret, as.raw(0L))
232
  ret
233

Guolin Ke's avatar
Guolin Ke committed
234
235
236
}

lgb.check.r6.class <- function(object, name) {
237

238
239
  # Check for non-existence of R6 class or named class
  all(c("R6", name) %in% class(object))
240

Guolin Ke's avatar
Guolin Ke committed
241
242
243
}

lgb.check.obj <- function(params, obj) {
244

245
  # List known objectives in a vector
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
  OBJECTIVES <- c(
    "regression"
    , "regression_l1"
    , "regression_l2"
    , "mean_squared_error"
    , "mse"
    , "l2_root"
    , "root_mean_squared_error"
    , "rmse"
    , "mean_absolute_error"
    , "mae"
    , "quantile"
    , "huber"
    , "fair"
    , "poisson"
    , "binary"
    , "lambdarank"
    , "multiclass"
    , "softmax"
    , "multiclassova"
    , "multiclass_ova"
    , "ova"
    , "ovr"
    , "xentropy"
    , "cross_entropy"
    , "xentlambda"
    , "cross_entropy_lambda"
    , "mean_absolute_percentage_error"
    , "mape"
    , "gamma"
    , "tweedie"
277
278
279
280
281
    , "rank_xendcg"
    , "xendcg"
    , "xe_ndcg"
    , "xe_ndcg_mart"
    , "xendcg_mart"
282
  )
283

284
  # Check whether the objective is empty or not, and take it from params if needed
285
286
287
  if (!is.null(obj)) {
    params$objective <- obj
  }
288

289
  # Check whether the objective is a character
290
  if (is.character(params$objective)) {
291

292
    # If the objective is a character, check if it is a known objective
293
    if (!(params$objective %in% OBJECTIVES)) {
294

295
      # Interrupt on unknown objective name
296
      stop("lgb.check.obj: objective name error should be one of (", paste0(OBJECTIVES, collapse = ", "), ")")
297

Guolin Ke's avatar
Guolin Ke committed
298
    }
299

300
  } else if (!is.function(params$objective)) {
301

302
    # If objective is not a character nor a function, then stop
303
    stop("lgb.check.obj: objective should be a character or a function")
304

Guolin Ke's avatar
Guolin Ke committed
305
  }
306

307
308
  # Return parameters
  return(params)
309

Guolin Ke's avatar
Guolin Ke committed
310
311
}

312
313
# [description]
#     make sure that "metric" is populated on params,
314
#     and add any eval values to it
315
316
# [return]
#     params, where "metric" is a list
Guolin Ke's avatar
Guolin Ke committed
317
lgb.check.eval <- function(params, eval) {
318

319
320
  if (is.null(params$metric)) {
    params$metric <- list()
321
322
  } else if (is.character(params$metric)) {
    params$metric <- as.list(params$metric)
323
  }
324

325
  if (is.character(eval)) {
326
    params$metric <- append(params$metric, eval)
327
  }
328

329
330
331
332
  if (identical(class(eval), "list")) {
    params$metric <- append(params$metric, unlist(eval))
  }

333
  return(params)
Guolin Ke's avatar
Guolin Ke committed
334
}