NEWS


kits 0.25.2

Faster: speed fixes only. Every result is identical to 0.25.1 (checked with identical() on the objects and on the plotly JSON of the plots, on the demo data and on a simulated set of 1 million responses). Times below are for that set.

kits 0.25.1

kits 0.25.0

The algorithm layer: algo_

kits_algorithm() replaces kits_code() and kits_equations()

Person scores: the Jeffreys modal estimate

Fewer functions for the same questions

Norms: nlminb

kits_raw_equate() removed

Observed item curves: plot(item, type = "observed")

One wave at a time: fixed steps, new top scores

Bug fixes

Less code

kits 0.24.1

Shorter help pages

Teaching scripts: the other cases

Less code

kits 0.24.0

The package shows short code and formulas; the teaching moves to fixed scripts and the tutorials.

kits_code(): an overview and teaching scripts

kits_equations(): short notes

Removed (announced in 0.23.0)

Tutorials and vignettes in Quarto

No derivative by hand

kits 0.23.1

The model is written once and RTMB gives every derivative. No estimate of kits (calibration, WLE, threshold SEs, latent normal of the link check, norms) rests on a derivative derived by hand any more; the information slope I' of kits_pcm_prob() (sum of third central moments) and the SE of the Goodman-Kruskal gamma of kits_item_restscore() (a published delta-method formula) still do. The calibration (CML), the links and the norms are unchanged to the last bit; WLE thetas and SEs move by at most about 1e-12 (except with two maxima, below); threshold SEs by at most about 1e-10.

Every derivative from RTMB

Speed

The WLE is now solved by RTMB, which tapes every operation, so it is slower. One thread:

| Design | Combinations | 0.23.0 | 0.23.1 | |---|---:|---:|---:| | KIT-like: 46,900 person-waves, 24 items, 7 waves | 1,230 | 2.9 s | 3.5 s | | 40 items, N = 3000, 10% missing at random | 2,965 | 0.9 s | 9.0 s |

The time is in building the Newton tape (about 2 ms per combination); the solve takes 0.03 s.

kits_code()

kits_equations()

Removed

Less duplicated code

kits 0.23.0

Item objects no longer carry a person-wave id

The a and b of a link are renamed intercept and slope

Read this first. A link is the line new value = intercept + slope x old value. Its elements are now called intercept and slope; kits 0.22.0 called them a (the intercept) and b (the slope). The letters a and b mean different things in different places (Kolen and Brennan, TAM, plink and equateIRT write A or a for the slope and B or b for the intercept), so kits no longer uses them. No number changes.

| Source | Intercept | Slope | |---|---|---| | kits 0.22.0 and before, the KIT link table 02_link_table.csv | a | b | | Kolen & Brennan (2014), TAM, plink, equateIRT | B (or b) | A (or a) | | kits 0.23.0 | intercept | slope |

| kits 0.22.0 | kits 0.23.0 | |---|---| | link$a, link$b | link$intercept, link$slope, or coef(link) (c(intercept = , slope = ), the order of lm()) | | link$a_error, link$b_error | link$intercept_error, link$slope_error | | link$a0, link$b0 | link$intercept0, link$slope0 | | link$ab_cov | link$vcov, a 2 x 2 matrix with dimnames intercept, slope (ab_cov is its off-diagonal) | | link$draws[, "a"], [, "b"] | link$draws[, "intercept"], [, "slope"] | | new value = a + b x old value | new value = intercept + slope x old value (print, kits_equations(), kits_code()) |

Old call -> new call

| Old call (0.22.0) | New call (0.23.0) | Status | |---|---|---| | link$a, link$b, a_error, b_error, a0, b0, ab_cov | intercept, slope, intercept_error, slope_error, intercept0, slope0, vcov; coef(link) | renamed; the old names stop with a clear error (kits_error_deprecated) until 0.24.0 | | person_id | visit_id (one child at one visit, a person-wave) | renamed; person_id in the data still read for one version, with a message (kits_message_data) | | pid | subj_id (the child) | renamed; pid still read for one version, with a message | | booklet_id, booklet_score, booklet_max | pattern_id, raw, raw_max | renamed (scores, raw-score tables, item$design, kits_stats()) | | "booklet" in prints, messages and help | "item pattern" (which items a person-wave answered); "standard form" stays | renamed | | item$stats$booklets, n_booklets of as.data.frame(item) | item$stats$patterns, n_patterns | renamed | | kits_booklet_table (class of kits_subj_table(item)) | kits_pattern_table | renamed | | kits_information(item, booklet_id = ), kits_expected_score(item, booklet_id = ) | pattern_id = | renamed argument | | kits_code(item, subj = TRUE) | kits_code(scores), scores <- kits_subj(X, item) | deprecated, removed in 0.24.0 | | — | kits_code(scores): the WLE and person fit from the item parameters the scores keep, then the conversions | new method | | — | kits_code(drift), kits_equations(drift), kits_equations("drift") | new methods | | kits_age50(), kits_mean_sigma(), kits_link_se(), kits_link_screening(), kits_wle_table(), kits_plot_cohort(), kits_plot_norms(), kits_item_positions(), kits_ladder_marks(), kits_plot_growth(), kits_plot_ladder() | see the 0.22.0 table below | removed | | kits_subj_table(item, data, wave_m), plot(norm, d = , age_m = ) | kits_subj(data, item, persons) \|> kits_subj_table(), plot(norm, scores = ) | removed (stop with an error naming the argument) | | s$z <- kits_norm_z(s, norm) | s <- s \|> kits_norm_z(norm) | removed (stops with an error that shows the new form) | | dataSrc =, D = (kits 0.17.0 names) | data =, d = | removed | | print(kits(...)) ended with the next steps of a calibration | the next steps after scoring (plot(x), x$subj \|> kits_subj_table(), kits_code(x), ...) | fixed | | kits_itemfit(X, item) on the calibration data | item$itemfit (the same table), plot(item, type = "fit"); kits_itemfit() stays for other data | new element |

Exported functions: 55 in 0.22.0, 48 now (the eleven deprecated functions are gone; kits_esf(), kits_item_restscore(), kits_lrtest() and kits_pcm_prob() are new).

Item fit travels with the item object, and is conditional

Shared cores and teaching functions

A core computation now lives in one internal function, and every part of kits calls it; the exported teaching functions show that same core.

Bug fixes

Details

kits 0.22.0

An API cleanup: one entry point for each task, so the package is simpler to learn. All numbers are unchanged from 0.21.0 (calibrations, links, scores, tables, norms, precision, equating and DIF are identical on a set of simulated objects computed with both versions). The old functions and forms still work in this version, give the same results as in 0.21.0, and warn with class kits_warning_deprecated naming the replacement. They will be removed in the next version (0.23.0). See ?kits_deprecated.

Old call -> new call

| Old call (0.21.0) | New call (0.22.0) | Status | |---|---|---| | kits_age50(pass, age_m, wave_m) | kits_age_pass(pass, age_m, wave_m)$p50 | deprecated, removed in 0.23.0 | | kits_mean_sigma(from, to, id, item) | points \|> kits_link_screen() \|> kits_link_line() \|> kits_link_error(), with points = data.frame(id, item, from, to) | deprecated, removed in 0.23.0 | | kits_mean_sigma(..., from_scale, to_scale) | kits_link_screen(points, from_scale = , to_scale = ) (new arguments) | deprecated, removed in 0.23.0 | | kits_mean_sigma(..., spread = "se", se_from, se_to) | columns se_from, se_to of points, kits_link_screen(spread = "se") | deprecated, removed in 0.23.0 | | kits_link_se(co, d_link, theta = x) | tibble(theta = x) \|> kits_convert(co, d_link, from = "lan M36") (its theta is the old value) | deprecated, removed in 0.23.0 | | kits_link_screening(link, theta) | summary(link, theta = )$screening (and printed by summary()) | deprecated, removed in 0.23.0 | | kits_wle_table(item) | kits_subj_table(item) | deprecated, removed in 0.23.0 | | kits_subj_table(data, item, wave_m), kits_subj_table(item, data, wave_m) | kits_subj(data, item, persons) \|> kits_subj_table() (from the scores) | form deprecated, removed in 0.23.0 | | kits_plot_cohort(waves, scores) | plot(kits_vertical(s), type = "median") | deprecated, removed in 0.23.0 | | kits_plot_norms(norms, scores) | kits_norms(s, by = "sex") \|> plot(scores = s) (several domains: by = c("domain", "sex")) | deprecated, removed in 0.23.0 | | kits_item_positions(items, norm), kits_ladder_marks(norm) | kits_item_table(thr, norms, age_pass = , dif = , stems = ) | deprecated, removed in 0.23.0 | | kits_plot_growth(norm, items) | plot(kits_item_table(...)) (type = "growth") | deprecated, removed in 0.23.0 | | kits_plot_ladder(items, marks) | plot(kits_item_table(...), type = "ladder", ages = ) | deprecated, removed in 0.23.0 | | s$z <- kits_norm_z(s, norm) | s <- s \|> kits_norm_z(norm): the scores with new columns z and pct (numbers in, numbers out as before) | form deprecated, removed in 0.23.0 | | plot(norm, d = s$theta, age_m = s$age_m) | plot(norm, scores = s); with a norm by group each panel draws the children of its group | form deprecated, removed in 0.23.0 | | ages <- kits_norm_ages(s), kits_norms(s, ages, pad = 0) | kits_norms(s): without ages the rule of kits_norm_ages() (and pad = 0); printed | ages optional | | kits_subj(X, item, persons = persons), kits_age_pass(pass, age_m, wave_m), kits_dif(X, sex) | the same, or first resp <- kits_resp(long), then kits_item(resp), kits_subj(resp, item), kits_age_pass(resp), kits_dif(resp, by = "sex"), kits_itemfit(resp, item), kits_inter(resp) | new function | | a matrix without row names, a data.frame without person_id: error | the rows are numbered 1, 2, 3, ... with a message | new behaviour | | kits_subj(..., persons) keeps wave_m, age_m, sex, cohort | also pid (the child id) | new column | | plot(kits(X)) (Wright map) | the same, or plot(item, type = "wright", scores = s) | new view | | confint(item), vcov(item), ... (none) | confint() of a kits_item, link and kits_dif; vcov(), simulate() of a kits_item; logLik(), nobs() (so AIC(), BIC()) and update() of a kits_item and a kits_norms | new methods | | dplyr::bind_rows() of kits tables | rbind() or purrr::list_rbind(), which keep the records of every table; bind_rows() keeps the first table's and warns | new warning | | kits_anchors(...) | the same, with a new domain column (lan, motor, cog, from the GSED ids) | new column | | the domain of a table in a score column (the stacked norms of kits_plot_norms(), the waves of kits_plot_cohort(), the items and norm of kits_item_positions(), kits_plot_growth(), kits_plot_ladder(), kits_ladder_marks()) | a domain column: kits_norms(by = c("domain", "sex")), list_rbind(names_to = "domain") of kits_vertical() results, the domain column of kits_item_table() and of the norms it takes; score is left for a score (as in item_score, booklet_score) | renamed; the deprecated functions still take and give score | | kits_linking(items, anchors) with a domain without anchors | the same line (as before); now printed (same line, no anchors: social) and recorded in kits_info() and kits_code() | new note | | plot(kits_vertical(s)) (mean +/- SD) | the same, or plot(v, type = "median") (median, P25 to P75); one panel per domain; cohorts, age_ticks, y_range; a note when the median age differs from the wave | new options | | fit one norm per sex yourself and stack | kits_norms(s, ages, by = "sex"), or by = data.frame(sex = sex) with scores as numbers | new argument | | kits_norm_z(s, norm) | with a norm by group each child gets the curves of its group (by = with numbers) | new argument | | plot(norm) | the same; a norm by group draws one panel per domain x group | new view | | — | plot(item, type = c("category", "expected"), items = ): category probability curves | new method | | — | plot(dif, items = , cut = 0.3): DIF per item with its 95% CI | new method | | — | plot(fit, type = c("both", "infit", "outfit"), items = , range = c(0.6, 1.4)): item fit | new method | | — | plot(kits_age_pass(...), items = ): ages at which children pass, easy to hard | new method | | — | plot(drift, type = c("scatter", "difference"), items = ): drift | new method | | — | plot(screen): share at the lowest score per wave | new method | | kits_vertical(s) | the same, with new columns P25 and P75 | new columns | | kits_information(item), kits_expected_score(item) | the same; now S3 generics with methods for kits_item, kits, kits_fit and a table of parameters | unchanged interface | | ?kits_stats, ?kits_cml, ?kits_thresholds | one page, ?kits_item_steps | help merged | | ?kits_link_points, ?kits_link_screen, ?kits_link_line, ?kits_link_error | one page, ?kits_link_steps | help merged | | ?kits_norm_data, ?kits_norm_fit, ?kits_norm_table | one page, ?kits_norm_steps | help merged |

Exported functions: 53 in 0.21.0, 55 now (new kits_resp() and kits_item_table()). The eleven deprecated functions are still exported in this version, so 44 remain after 0.23.0. The new plots and base methods are S3 methods (plot(), print(), summary(), confint(), ...), not new functions. Every plot() method chooses a view with type = (the first value is the default) and the items with items = (item ids).

Details

Round 3: data first, models as tools

Data

kits 0.21.0

The WLE is computed in base R, without RTMB; the CML stays in RTMB. The numbers of 0.20.0 are unchanged: the item parameters are identical, and theta and se differ by at most 2e-11. On the real KIT data (five domains, two cohorts, 296,343 person-waves) the largest difference is 1.9e-11, and scoring all ten calibrations takes 20.2 s instead of 26.0 s.

WLE in base R

CML stays in RTMB

Time (one thread, median of 3 runs)

Simulated data (kits_sim()). KIT-like: 7 waves of 6,700 person-waves, each with a targeted form of 7 or 8 of 24 items (1 to 3 steps), plus random missing responses. Expert review: 40 items (20 with 2 steps), N = 3000, 2% or 10% of the responses missing at random. dexter 1.8.1: fit_enorm() and ability(method = "WLE") on the long data.frame (kits takes the matrix and converts it inside the timing).

| Design | Booklets | kits_item() 0.20.0 | 0.21.0 | dexter fit_enorm() | kits_subj() 0.20.0 | 0.21.0 | dexter ability() | |---|---:|---:|---:|---:|---:|---:|---:| | KIT-like | 59 | 1.66 s | 1.65 s | 0.13 s | 2.12 s | 1.97 s | 0.06 s | | KIT-like | 132 | 1.71 s | 1.69 s | 0.19 s | 2.72 s | 1.96 s | 0.06 s | | 40 items, 2% missing | 514 | 2.90 s | 2.91 s | 1.27 s | 4.83 s | 0.47 s | 0.07 s | | 40 items, 10% missing | 2,689 | 12.81 s | 12.76 s | 5.65 s | 10.81 s | 0.75 s | 0.24 s |

The WLE alone (all combinations of a design): 0.17 -> 0.03 s, 0.65 -> 0.06 s, 4.77 -> 0.22 s and 9.43 -> 0.49 s. In the KIT-like designs the rest of kits_subj() (about 1.9 s) is the data handling: the item set of each person-wave (0.8 s) and the person fit (0.9 s).

The scripts of kits_code()

kits 0.20.0

kits and kitsPreview now share one version number, starting at 0.20.0 (kits skips 0.18.1 and 0.19; kitsPreview jumps from 0.11).

A fix of the linking error in a pipe of kits_convert(), and documentation. The numbers of 0.18.0 are unchanged.

Linking error in a pipe

Documentation

kits 0.18.0

Links and norms in named steps, one-row summaries with as.data.frame(), and the design items of the expert review (Part B): overlapping person ids in kits_link_check(), a faster CML, a flag for perfect separation, consistent argument names, and shorter code lines. The numbers of 0.17.0 are unchanged, except the last digits of the CML (below 1e-10; see "CML with many booklets").

Links and norms in named steps

as.data.frame(): one-row summaries

B1: overlapping person ids in kits_link_check()

B2: CML with many booklets

B4: perfect separation in kits_age_pass() and kits_age50()

B5: argument names and S3 dispatch

B6: style

Other changes

kits 0.17.0

Three changes: kits_convert() is taught with the pipe, the D-link anchors are package data, and kits_code() has three tiers. No number of 0.16.0 changed (every object compared in the tests).

kits_convert() with the pipe, one link per line

kits_anchors(): the anchors of the D link

kits_code() in three tiers

Calibration in three steps; the result packs key objects

kits 0.16.0

Fixes from three reviews (an expert review, a beginner's review and the privacy findings of the Shiny plan). The numbers of 0.15.0 are unchanged (to about 1e-13), except where a fix below says so.

Numbers that change on purpose

Privacy

Stacking tables

CML

Other fixes (expert review)

Messages and defaults (beginner review)

Documentation

kits 0.15.0

Code style: tidyverse internals; dependencies added. The numbers of 0.14.0 are unchanged, apart from the fixes below.

Code style

Fixes

kits 0.14.0

The second batch of methods from Kolen and Brennan (2014), Test equating, scaling, and linking (3rd ed.). Page numbers below are the printed pages of the book. The defaults are unchanged, so every result of 0.13.0 (and the agreement with kitsPreview) stays the same.

New features

Other changes

kits 0.13.0

The first batch of methods from Kolen and Brennan (2014), Test equating, scaling, and linking (3rd ed.). Page numbers below are the printed pages of the book.

New features

Other changes

kits 0.12.0

Breaking changes

New features

Other changes

kits 0.11.0

Breaking changes

New features

Other changes

Bug fixes