irtQ 1.4.0
Bug Fixes
ctt()returned a standardized alpha (alpha_std) that was too large, and could exceed 1, when a test included a constant item. The constant item is now kept in the item count as in the raw alpha, and the value is 0 when only one item varies, as is the raw alpha. An undefined standardized alpha or mean discrimination isNAinstead ofNaN.ctt(),ctt_distr(), andfreq_score()silently left out of their counts a score within rounding error of a whole number, such as(0.1 + 0.2) * 10. Such a score is now counted as that whole number.freq_score()tabulated a factor by its level codes (1, 2, 3, …) instead of its labels. A factor is now converted through its labels.ctt_distr()listed an option twice in selected-response mode when the responses wrote it in different ways, such as"1"and"01", which inflated the frequencies and percentages. The option is now listed once.score_resp()andctt_distr()did not treat a response with surrounding spaces, such as" 9", as missing whenmissing = "9". Responses are now compared withmissingafter trimming spaces.score_resp()andctt_distr()silently scored every response to an item as wrong when its key value contained a comma, such as"1,5". They now stop with an error, because every item must have exactly one correct option.ctt()did not flag an item whose discrimination is undefined, such as an item with a constant score, so the item could pass thecrit.discheck. Such an item is now flagged with"discrimination undefined".shape_df_fipc()misplaced the parameter columns of the new items when the fixed items included a polytomous item and every new item was dichotomous, which changed a threshold of a fixed polytomous item. The new items now follow the columns of the fixed items. FIPC results built from such metadata should be rerun.The GPCM probabilities of
prm(),traceline(), andinfo()were distorted when an exponent exceeded 700, and the values at the other thetas and items of the same call changed with them. Each row is now rescaled by its own maximum. Results in the usual range change by less than 1e-12, andprm()no longer returns a GPCM probability of exactly 0.info()returnedNaNinstead of 0 for a dichotomous item at a theta far from the item location.traceline()returned a vector instead of a one-row matrix inprob.catsfor a polytomous item when a single theta was given, which madeplot()fail.drm()checked each probability against the guessing parameter of another item, and the expected item score oftraceline()included categories that the item does not have. Probabilities and expected scores could differ by about 1e-10, and item parameter estimates by about 1e-9.traceline()andinfo()stopped with an error for a two-category GRM or GPCM item, such as one made bybring.mirt(), and for a 3PLM or DRM item with anNAguessing parameter, for whichdrm()returnedNA. AnNAguessing parameter is now treated as 0, so a two-category GRM or GPCM item is computed as a 2PLM item, also when the test has polytomous items of the same model.shape_df()used a singlegvalue for the first dichotomous item only and set the guessing parameters of the other items to 0. A singlegvalue is now used for every dichotomous item, as insimdat(), and agvector of any other wrong length stops with an error.est_score()withncore > 1failed when a chunk of examinees had a single row, for example with 3 examinees andncore = 2, and it did not close the cluster when a worker failed. Each chunk is now kept as a matrix, also when it has one row, and the cluster is always closed.est_score()withmethod = "ML","WL","MLF", or"MAP"could stop at a point that was not a solution when the iterations alternated between two values, which happened mostly for low-scoring examinees on tests with 3PLM items. The estimate then depended onmax.iter. When the iterations do not converge, the root of the score function is now found within the range of the visited values.est_score()withmethod = "INV.TCC"returned wrong standard errors whenintpol = FALSEor when the interpolation was skipped with a warning (arange.tccthat is too narrow), because the estimates were paired with the wrong sum scores of the score distribution. The standard deviation is now taken over the sum scores that have an estimate. Results withintpol = TRUEand no warning are unchanged.est_score()withmethod = "MLF"gave an examinee with all responses missing an estimate near 0 and a standard error of 316.2 without a warning. The examinee now getsNAand the warning, as with the other methods.est_score()withmethod = "EAP"returnedNaN, with a warning about missing responses, for tests of about 1,000 items or more, because the product of the item probabilities underflowed. The posterior is now computed from log-likelihoods, and results for usual tests change by less than 1e-14.llike_score()withmethod = "MLF"stopped with an error under the defaultfence.b = NULL. The fences are now placed at -5 and 5, the defaultrangeofest_score(). The function also merged examinees whose rows had the same row name, and an examinee without any response, who stopped the function ("ML","MAP") or got values from the fence items alone ("MLF"), now getsNA.lwrc()stopped with an error for a singlethetawhen the test had polytomous items.sx2_fit()counted some summed score groups twice in the tables of polytomous items when the test was so short that the groups merged at the two ends overlapped (for example, two items with five categories). The observed and expected frequencies and the degrees of freedom were too large. Each summed score group is now counted once.sx2_fit()andirtfit()reported a p-value of 0 (orNaNwith a warning) for an item without degrees of freedom left after collapsing, which read as a strong misfit. The critical value and p-value of such an item are nowNA, with a warning that names the item.sx2_fit()named the columns of the observed frequency and proportion tables (obs_freqandobs_prop) with anexp_freq.prefix (exp_freq.score.0, …) when the test had a single polytomous item. The columns are now namedscore.0,score.1, … as for tests with several polytomous items. The values are unchanged.plot.irtfit()ignoredxlab.textwhentype = "both"and always used theta as the x-axis title. The given title is now used, as for the other types.plot.irtfit()drew the standardized residuals as blue crosses, instead of red circles, when every plotted residual exceededoverSR. Each kind of point now keeps its color and shape.irtfit()stopped with an error for a logical response matrix, andirtfit()andsx2_fit()stopped with an error when the groups of an item were merged into a single row (for example, with a largemin.collapse). These cases now work; an item left without degrees of freedom getsNAfor the critical value and p-value of the chi-square statistic, with a warning.sx2_fit()for anest_irtobject computed the expected frequencies with a standard normal distribution even when a different latent distribution was used or estimated in the calibration (for example, withEmpHist = TRUEor FIPC). Whenweights,norm.prior, andnquadare all omitted, the latent distribution stored in the object is now used. The statistics change slightly for a calibration with a standard normal distribution and can change enough to alter the decision atalphawhen the distribution was estimated. Setnorm.prior = c(0, 1)to reproduce the earlier results.run_mst()stopped with an obscure error for a panel whose routing module is not module 1 whenini_modwasNULL, because the starting module was drawn withsample()from a single module number. The examinees now start in the routing module.reval_mst()took the score distribution of the first stage from module 1 even when the routing module had another number, which gave wrong results without an error. The routing module is now taken fromroute_map, and a first stage with more than one module stops with an error.run_mst()still runs such panels.cac_lee()andcac_rud()used weights that did not sum to 1 as given, so the marginal indices could exceed 1. Such weights are now rescaled to sum to 1, with a warning. Weights that are missing, infinite, or negative, or that have a non-positive sum, now stop with an error.plot.find_cut()withtheta_rangestill stretched the x-axis to the crossing lines outside the range. The x-axis now covers the requested range.ripd()stopped with an obscure error when an examinee had no ability estimate: a missing value inscore, an examinee without any response, a focal group examinee left out bymin.resp(somin.respcould not be used), or an examinee whose responses were all removed during purification. Such examinees are now excluded, and the function stops when no examinee has an ability estimate. A warning reports the examinees excluded because ofscoreormin.resp, and one warning at the end of the purification reports the examinees excluded during it.min.respnow also applies to the first analysis whenscoreis given, and an examinee with a missing value inscorestays out of the purification. The purification stops with a warning andcomplete = FALSEwhenmin.resp, which counts the items that remain, leaves a group without any ability estimate;scoreand the number of examinees reported as excluded then belong to the last iteration whose statistics are reported.ripd()andpcd2()stopped with an obscure error when purification left a single item. They now work. When every item is flagged, the purification stops with a warning and returns all items as flagged withcomplete = FALSE.ripd()andpcd2()silently accepted responses outside the score categories (for example, an unrecorded missing code such as -9, which gave chi-square values in the hundreds of thousands), agroupof the wrong length (ripd()), and anitem.skipthat was not a set of item positions (an ID given topcd2()added a spurious row and skipped nothing). They now stop with a clear message, a logicalitem.skipis read as item positions, also during purification, and character responses are read as numbers.pcd2()warned about excluded items even whenmin.respexcluded none, and worded the warning poorly. It now warns only when items are excluded.ripd()compared the p-values rounded to four decimals withalpha, so an item with a p-value just abovealpha(for example, 0.05004 foralpha = 0.05) was flagged. The decision now uses the unrounded p-values, and the tables still show four decimals.ripd()computedRIPD_RSfrom arbitrary values when the covariance matrix ofRIPD_RandRIPD_Swas singular, which happens when all examinees who responded to an item have the same probability of a correct response (a tiny constant was added to every entry of the matrix). The statistic is now computed with a generalized inverse and the rank of the matrix as degrees of freedom, and a warning names the items. Because the degrees of freedom can then differ between items, the purification removes the flagged item with the smallest p-value of thepurify.bystatistic, compared on the log scale, instead of the item with the largest statistic. Results for regular data are unchanged.rdif()andcrdif()stopped with an obscure error when an examinee had no ability estimate: a missing value inscore, an examinee without any response, an examinee left out bymin.resp(somin.respcould not exclude any examinee), or an examinee whose responses were all removed during purification. Such examinees are now excluded, and the functions stop when no examinee has an ability estimate. A warning reports the examinees excluded because ofscoreormin.resp, and one warning at the end of the purification reports the examinees excluded during the purification.rdif()andcrdif()stopped with an obscure error when purification left a single item, andrdif()also stopped with “non-conformable arguments” when purification removed the item with the most score categories from a test with polytomous items. They now work. When every item is flagged, the purification stops with a warning and returns all items as flagged withcomplete = FALSE. The purification also stops with a warning andcomplete = FALSEwhenmin.resp, which counts the items that remain, leaves a group without any ability estimate;scoreand the number of examinees reported as excluded then belong to the last iteration whose statistics are reported.rdif()andcrdif()silently accepted responses outside the score categories (for example, an unrecorded missing code such as -9, which flagged every item), agroupof the wrong length, afocal.namethat is not ingroup, anitem.skipthat was not a set of item positions (a logicalitem.skipskipped only the first item during purification), analphaoutside (0, 1), amax.iterthat was not a positive whole number whenpurify = TRUE, and agroupwith more than two values (the values other thanfocal.namewere pooled into the reference group). They now stop with a clear message; usegrdif()to compare more than two groups. A logicalitem.skipis read as item positions, also during purification, and character responses are read as numbers.rdif()compared the p-values rounded to four decimals withalpha, so an item with a p-value just abovealpha(for example, 0.05004 foralpha = 0.05) was flagged, and an item with a p-value below 0.00005 was flagged even for analphaof 1e-30. The decision now uses the unrounded p-values, and the tables still show four decimals.rdif()computedRDIF_RSfrom arbitrary values, including negative chi-square values, when the covariance matrix ofRDIF_RandRDIF_Swas singular, which happens when all examinees who responded to an item have the same probability of a correct response (a tiny constant was added to every entry of the matrix). The statistic is now computed with a generalized inverse and the rank of the matrix as degrees of freedom, and a warning names the items. Results for regular data are unchanged.crdif()comparedRDIF_R-CRandRDIF_RS-CRof polytomous items with chi-square distributions with K and 2K degrees of freedom, although the category residuals of each examinee sum to zero, so the covariance matrices have ranks K - 1 and 2K - 1. The tests were therefore conservative (about 2% to 3% of the DIF-free items were flagged byRDIF_R-CRandRDIF_RS-CRatalpha = 0.05). The degrees of freedom are now K - 1, K, and 2K - 1, and the statistics are computed without adding a small constant to a singular covariance matrix, which changed them in the fourth decimal. Results for dichotomous items are unchanged.The purification of
crdif()removed the item with the smallest p-value, which was the item listed first when several p-values were too small to be represented (they were 0). The purification ofrdif()andcrdif()now removes the flagged item with the smallest p-value of thepurify.bystatistic, compared on the log scale, so the choice also holds for an item ofrdif()whoseRDIF_RShas fewer degrees of freedom because of a singular covariance matrix. Results for regular data are unchanged.rdif()andcrdif()ignoredmin.respin the initial analysis whenscorewas supplied.min.respis now applied from the initial analysis, with or withoutscore: the scores of the examinees with fewer thanmin.respresponses are set to missing and these examinees are excluded.grdif()stopped with the error “object ‘chisq’ not found” when a group had no response to an item. The three GRDIF statistics and their p-values of such an item are nowNaN, and the item is not flagged.grdif()stopped with an obscure error when an examinee had a missing ability estimate, andcatsib()left such an examinee out without a message. Both functions now exclude the examinee with a warning, and stop with a message when no examinee has an ability estimate.grdif()put the examinees whose group is missing into the reference group, while its post-hoc analyses left them out. They are now left out of every analysis, as in the other DIF functions.The purification of
grdif()andcatsib()stopped with an obscure error when one item was left, so it could not end when every item was flagged. It now stops with a warning andcomplete = FALSEwhen every item is flagged or when a group has no ability estimate left (catsib(): fewer than two withinrange, which happens when a single item is left andmethod = "ML"), and the remaining items keep the results of the last counted iteration, as inrdif()andcrdif(). The flagged item with the smallest p-value is removed at each iteration, as inrdif()andcrdif(); for regular data this is the item removed before. One warning reports the examinees who lost their ability estimates during the purification, the warning for an incomplete purification reads as that ofrdif(), andn.iterof thewith_purifyresults is the integer 0 when no item is flagged in the initial analysis.grdif()andcatsib()silently computed wrong statistics for some invalid inputs, such as character responses, responses outside 0 and 1, or agroup,score, orsewhose length does not matchdata, and stopped with an obscure message for others, such as afocal.namethat is not found ingroupormethod = "EAP.SUM"or"INV.TCC". They now stop with a clear message, also for analphaoutside (0, 1), amax.iterthat is not a positive whole number whenpurify = TRUE, or an invalidmin.resp. Theest_itemmethod ofgrdif()stops whenscoreis passed, which it ignored before, and theest_irtandest_itemmethods stop whendataorDis passed.catsib()also stops whenmin.binsizeis not an integer of 2 or more (with 1, the p-values of all items were missing), whenn.binis not the maximum and the minimum number of bins in this order, and whenmax.delis not from 0 to less than 1.A logical
item.skipofcatsib()skipped the wrong items during the purification, so a skipped item could be tested, flagged, and removed. It is now read as item positions, also during the purification.grdif()andcatsib()compared the p-values rounded to four decimals withalpha, so an item whose p-value was within rounding ofalphacould be flagged wrongly, and the purification could remove it. The unrounded p-values are now compared; the tables still show four decimals.grdif()computedGRDIF_R,GRDIF_S, andGRDIF_RSafter adding small constants to the covariance matrix of an item when the matrix was singular or nearly singular, for example when all examinees of a group had the same ability estimate, and could stop with an error. The statistics now use a generalized inverse, the degrees of freedom are the rank of the matrix instead of G - 1, G - 1, and 2(G - 1), and one warning names the items. Results for regular data are unchanged.catsib()returned a statistic of 0, a standard error of 0, and a p-value ofNaNfor an item when the corrected ability estimates of the two groups did not overlap, and ended the purification as complete. The statistics of such an item are nowNA, one warning names the items, and the item is not flagged. The purification stops with a warning andcomplete = FALSEwhen this happens to every remaining item.The estimation of GPCM items with large slopes in
est_irt(),est_mg(), andest_item()rescaled all cumulative logits by a common factor once one exceeded 700 in absolute value, which distorted the objective function, and the gradient and the Hessian could becomeNaNeven for logits well below 700. Each row is now shifted by its own maximum when a logit exceeds 100 in absolute value; results below that range are unchanged.est_irt()andest_mg()computed the standard errors of GRM and GPCM items withD = 1whateverDwas. WithDother than 1, such as 1.702, the standard errors of polytomous items were about 1.4 to 2 times too large, those of the dichotomous items of the same test were also affected, and so werecovarianceandtest.2. The estimates were not affected.With
fix.g = TRUEand ag.valother than 0.2,est_irt()andest_mg()computed the standard errors of 3PLM items as if the guessing parameter were 0.2. The error grows with the distance betweeng.valand 0.2, for example about 9 percent withg.val = 0.25and about 25 percent with 0.05 or 0.35 for theLSAT6data. The estimates were not affected.When
Dwas not 1, the Hessian of 1PLM items with a common estimated slope (fix.a.1pl = FALSE) had a wrong cross term between the slope and the difficulties. Inest_item()the standard error of the slope came out too small and the optimization could stop short of the maximum; inest_irt()andest_mg()the M-step followed a different path, which changed the number of EM cycles and the estimates within the convergence tolerance. The Hessian is now correct, and results forD = 1are unchanged.covirt()underestimated the standard errors of polytomous items by about 10 to 25 percent, because it computed their information with Equation A8 of Li and Lissitz (2004), which treats the category indicators of a response as independent Bernoulli variables. It now uses the information of the multinomial response, whose standard errors agreed with the empirical standard deviations of the estimates in a simulation; see Details ofcovirt(). The results for dichotomous items do not change.covirt()stopped with an error for a GRM or GPCM item with two score categories. Such an item is now treated as a 2PLM item, and a GPCM item listed inpcm.locas a 1PLM item with a fixed slope.covirt()printed a message instead of issuing a warning when the information matrix of an item was singular, and the matrix and the vector of standard errors filled withNAhad no names. It now issues a warning, and theNAvalues carry the parameter names.In a test with many items, such as several hundred, the likelihood of an examinee could underflow to 0 at every quadrature point.
est_irt()andest_mg()then stopped with “NA/NaN gradient evaluation”, andpcd2()returnedNaNfor every item. The likelihood of such an examinee is now rescaled by its largest log-likelihood, and the marginal log-likelihood adds the scale back. Results for tests where no likelihood underflows are unchanged.pcd2()also no longer returnsNaNwhen a quadrature point has no posterior mass.est_irt(),est_mg(), andest_item()could return the thresholds of a GRM item in a decreasing order, which is not a valid solution because the category probabilities then sum to more than 1 and the reported log-likelihood is inflated. It happened mostly in small samples or when a score category has few responses (for example, in 13 of 40 simulated data sets with five categories and 300 examinees). When the unconstrained estimates are not in increasing order, the item is now estimated again with its thresholds kept in increasing order. Results of items whose thresholds were already ordered are unchanged. Fits with a GRM item whose thresholds were reversed should be rerun.When the second column of
weightsdid not sum to 1,est_irt()andest_mg()reported a log-likelihood, AIC, and BIC that were off byNtimes the log of the sum, and a variance ingroup.parthat was multiplied by the sum. WithEmpHist = TRUEthe empirical histogram was rescaled to that wrong variance, which changed the scale of the item parameters (by up to 1.8 in an example with a sum of 4). The weights are now rescaled to sum to 1 with the warning “The weights do not sum to 1 and were rescaled to sum to 1.”, as incac_lee()andcac_rud(); weights that are negative, not finite, or sum to zero stop with an error. Weights that sum to 1 give the same results as before.With
EmpHist = TRUEand userweightswhose number of quadrature points is not 49 (the default ofQuadrature[1]),est_irt()andest_mg()stopped with an error such as “non-conformable arguments” when there were fewer points, and extrapolated the upper tail of the empirical histogram from the wrong point when there were more. The number of points is now taken fromweights. The results for 49 points are unchanged.est_irt()withfipc = TRUEcounted a fixed item twice in the first E-step without notice whenfix.loclisted it more than once, which changed the estimates of the items to calibrate (by up to 0.45 in an example withfipc.method = "OEM"). It now stops with an error.With
fipc = TRUE, all items fixed, and userweights, the change in the first EM cycle was measured from the mean and variance of the default distribution instead of those ofweights, which could shift the cycle at which the iteration stopped. It is now measured from the moments ofweights.npar.est,aic, andbicofest_irt()andest_mg()did not count the weights of the empirical histogram (EmpHist = TRUE) as free parameters, andest_irt()with FIPC counted only the mean and variance of the latent distribution (2 values) even withEmpHist = TRUE. With 49 quadrature points, the AIC of a model with an empirical histogram was 92 too small (2 times the 46 weights), which made it look better than a model with a normal distribution. The count now includes the free weights: Q - 3 for a histogram rescaled to a fixed mean and variance (est_irt()without FIPC, and the reference groups ofest_mg()) and Q - 1 for a histogram that is not rescaled (est_irt()with FIPC, and the free groups ofest_mg()), where Q is the number of quadrature points. The values for a normal distribution do not change.est_mg()withx = NULLstopped with an error whencats = NULL, although the help page allows it when all models are dichotomous, and whenmodelincluded"DRM". Both now work, as inest_irt().est_mg()counted a group that was listed twice infree.grouptwice innpar.est, which madeaicandbictoo large. It silently treated a group name infree.groupthat is not ingroup.nameas a reference group, and it silently dropped a position infix.locthat is outside the rows ofx, so that a different set of items was fixed. A repeated group is now counted once, and the other two cases stop with an error.est_mg()ran with a response such as 0.5, which the likelihood and the frequency tables treated differently. Responses must now be whole numbers from 0 to the number of score categories minus 1, as inest_irt().With
missingsupplied, thegroupelement ofdatareturned byest_mg()still held the missing-value code, whileoverallhadNA. Both now haveNA.est_item()stopped with an unclear error whenscorewas a data frame, contained a missing value, or had a length different from the number of rows ofdata, and when only one item had responses. A data framescorenow works, and the other cases stop with a clear message or work. The message about items that did not converge numbered the items after the items without responses had been removed; it now uses the positions in the supplied data.est_mg()withfipc = TRUEalways stopped with an error whenitem.idwas supplied: with “do not share common items” when the IDs were those ofx, and with “At least one model name is mis-specified” when they were different (present in 1.3.1). A supplieditem.idnow replaces the IDs inx, as the help page says, andfix.idrefers to the new IDs.FIPC with fixed items whose responses are all the same (for example, all correct) produced an error (“NA/NaN gradient evaluation”) with
fipc.method = "MEM", degenerate estimates (a slope of 0.01 and a difficulty above 10) with"OEM", andNaNgroup parameters after 500 cycles when all items were fixed. Such fixed items carry no information to place the ability scale.est_irt()andest_mg()now stop with a message when the responses to all fixed items show no variation (est_mg()uses the responses pooled over the groups). A failed short search for starting values in the first two EM cycles is now skipped, and when the last search of an item in an M-step fails, the item keeps its previous values and is reported as not converged; both cases stopped the run before.For
est_mg()with FIPC and all items fixed,test.2read “Solution is a possible local maximum.” although no information matrix is computed. It now reads as inest_irt(): “No item parameters are estimated, so the information matrix is not computed.”est_item()returned the log-likelihood plus the log prior densities asloglikelihoodwhen prior distributions were used, for example for 3PLM items with the defaultuse.gprior = TRUE, although the help page and the printed “-2loglikelihood” call it the log-likelihood. It now returns the log-likelihood at the final estimates without the log prior densities, asest_irt()andest_mg()do. The estimates, standard errors, and covariances are unchanged, and so isloglikelihoodwhen no prior is used.bring.mirt()returnedNAparameters, or silently wrong ones, for some mixed-format fits (present in 1.3.1). The columns of the coefficient matrix of mirt follow the model of the first item, so a fit that began with a polytomous item gaveNAfor its dichotomous items, and a fit that used the graded or gpcm model for every item, including two-category items, gave wrong values (the category probabilities differed by up to 0.5). The parameters of each item are now read by the column names. The interceptcof the gpcmIRT model, which was dropped, is moved into the thresholds (b - c / a); it changes the thresholds only whencis not 0. A dichotomous item of an unsupported model in a mixed-format fit, such as a 4PL item, now stops with an error instead of losing its upper asymptote. A model fitted to multiple groups, which is not supported, now stops with a message that asks for one extracted group (mirt::extract.group()).write.flexmirt()wrapped the lines of the file at the width of the console (80 characters inRscript), which broke the file:bring.flexmirt()could not read it, and flexMIRT would not either. This happened for the lines of items with several categories (GRM and GPCM), so the files written by the examples of the help page were affected. Each item and each group is now written as one line, and the file does not depend on the console width.write.flexmirt()wrote a wrong first step parameter for GPCM items with a number of categories other than five (the difference was up to 0.69 for the four-category items ofsimCAT_MX), because the mean of the step parameters was computed with a constant 4 instead of the number of steps. WithrePar = TRUE, it also left out the last scoring function value of GPCM items. Files written for GPCM items with five categories are unchanged. A GPCM item with two categories andrePar = FALSEstopped with a subscript error; it is now written as a two-parameter item. The file is also closed when the function stops with an error.est_irt()stopped with an out-of-range error for response columns stored as character, such as"0"and"1", andest_mg()stopped with an error when only some of its groups had character columns (present in 1.3.1). Character and factor columns are now read by their values inest_irt()andest_mg(), as inest_item(). A non-numeric missing code, such asmissing = ".", is recoded before the columns are read inest_irt(), as inest_mg().
Changes in Behavior
catsib()has a new argumentrho.screen,TRUEby default. When the ability estimates of a few examinees have very large standard errors, the estimated reliability (rho^2) of the regression correction can be far below the reliability of the other estimates, or negative, which shrinks the corrected abilities too much and inflates the Type I error rate. Withrho.screen = TRUE, the examinees whose squared standard error is equal to or greater than the variance of the ability estimates of their group are left out of the computation of the group mean, the variance, and the error variance used in the regression correction; they are still matched and tested. This changes the default results ofcatsib(). The warning about rho^2 falling below 0.05 is given less often, and once per call instead of at every purification iteration.rho.screen = FALSEcomputes rho^2 as earlier versions did.The post-hoc pairwise analyses of
grdif()take the reference group as the reference group of every pair that includes it, and, for a pair of focal groups, the group listed earlier infocal.name. Before, the group that came later in the sorted order of the group values was the focal group of a pair. When the two orders differ,group.pairis labeled in the new order, the signs ofrdifr,z.rdifr,rdifs, andz.rdifsare reversed, andn.refandn.focare swapped.rdifrs, the p-values, and the flags do not change.The default of
rangeingrdif()isc(-5, 5), as inrdif(),crdif(),ripd(),catsib(), andest_score(), instead ofc(-4, 4). The ability estimates of the examinees whose ML estimates reach a limit of the range (for example, all responses correct or all wrong) change, and so do the results that depend on them.grdif()supports dichotomous items only and stops with an error whenxincludes a polytomous item or any item of the “GRM” or “GPCM” model, even one with two score categories, because the GRDIF framework has been evaluated for dichotomous items (Lim et al., 2024). Before, polytomous items were analyzed as net DIF, and the default post-hoc analyses of mixed-format data could stop with an error.grdif()stops with an error when the examinees whose group is not infocal.namebelong to more than one group, because they form the reference group. Before, the GRDIF statistics combined them into one reference group while the post-hoc analyses compared them separately.grdif()andcatsib()applymin.respfrom the initial analysis also whenscoreis supplied: the scores of the examinees with fewer thanmin.respresponses are set to missing and these examinees are excluded, and a warning reports their number.est_irt()stops with a clear message that names the items when a response is not a whole number from 0 to the number of score categories minus 1. Before, it stopped with “subscript out of bounds”. Withfipc = TRUEit also stops whenxanddatahave different numbers of items, whenfix.locis empty or points outside the rows ofx, and when an ID infix.idis not found in the item IDs. Before, an unmatched ID infix.idwas dropped without notice.A supplied
item.idreplaces the IDs inxinest_irt()also whenfipc = FALSE, as it already did withfipc = TRUE. Before, it was ignored whenxwas supplied. It must have one value per item. The same holds forest_mg()(one vector of IDs per group, also without FIPC) andest_item().For
est_irt()with FIPC and all items fixed,test.2reads “No item parameters are estimated, so the information matrix is not computed.”, instead of the message about a possible local maximum.est_irt(),est_mg(), andest_item()issue one warning, with the item IDs, when an item to be estimated has a score category without responses or all its responses are the same. The maximum likelihood estimate does not exist for such an item, and the functions returned extreme or starting values with standard errors of 99999 without notice. The estimates are not changed.est_mg()judges an item on its responses pooled over the groups, andest_item()judges the items that have responses.bring.mirt()labels a two-category item fitted with the graded, gpcm, or gpcmIRT model as"DRM", with a guessing parameter of 0, because a two-category item is a dichotomous item in irtQ. Such an item was labeled"GRM"or"GPCM", with wrong or missing parameters (see Bug Fixes).The
typeargument ofbring.flexmirt(),bring.bilog(), andbring.parscale()is"par"when omitted, as the usage line suggests, and its case is still ignored. Omitting it stopped with “the condition has length > 1” before. An unknown type stops with the available choices.bring.parscale()stops with an error for the model codes 2, 4, 6, and 8 of PARSCALE when the file has polytomous items. The thresholds of such items were allNA.
Minor Improvements
est_mg()checks its inputs and stops with a clear message whengroup.namedoes not have one unique name per group, whenx,model,cats, oritem.iddoes not match the groups or the columns ofdata, when an item ID is repeated within a group, when items with the same ID have different models or numbers of score categories in different groups, whenfree.groupnames or numbers a group that does not exist or leaves no reference group without FIPC, whenfix.locis not a list with one element per group or has repeated positions, when an ID infix.idis not found, and whenfipc.methodis not"MEM"or"OEM"(also inest_irt()).est_mg()with FIPC warns when a fixed item has different parameter values in different groups. The values of the first group that includes the item are used, as before. Differences of at most 1e-10 are ignored.run_flexmirt()stops with a clear message on systems other than Windows, where flexMIRT does not run, and when “C:/Program Files” holds more than one flexMIRT folder (givedir.flexthen).write.flexmirt()stops with a clear message whenfileisNULL.getirt()can extract the matchedcallofest_irt(),est_mg(), andest_item()objects withwhat = "call".ctt()records thecorrectsetting in itscritelement, and thesummary()report names the item-total correlation used for flagging.ctt()andctt_distr()stop with a clear message whencrit.p,crit.dis,crit.distractor, ortotalis not valid, when fewer than two examinees remain, or whenctt_distr()receives numeric and non-numeric keys together.Inputs that were silently misread now stop with a clear message. Functions that take item metadata reject parameter columns stored as text, a missing or too small
cats, a dichotomous model withcatsother than 2, and a polytomous item with more thresholds thancats - 1, including any value inpar.3of a two-category GRM or GPCM item.shape_df()andsimdat()reject parameter vectors that do not match the items,shape_df_fipc()checksfix.loc,gen.weight()checksdistandtheta, andprm()rejects an unknownpr.model.prm()uses the GRM whenpr.modelis omitted,simdat()repeats a singlepr.modelfor all polytomous items,shape_df()acceptspar.drmwithoutg, and item metadata without apar.3column is accepted when every item is 1PLM or 2PLM.plot.info()andplot.traceline()show item IDs as given ("item-2"was shown asitem.2), and the panel titles ofplot.traceline()readScore: 0instead ofScore: resp.0.The warning for
"DRM"items reads “All ‘DRM’ items are treated as ‘3PLM’ items.” and no longer mentions item parameter estimation, because it also appears in functions such asinfo()andsimdat().est_mg()gives it once, not once for each group.est_score()stops with a clear message whendatahas a number of columns other than the number of items (it scored the wrong items before), when a response is not an integer within the categories of its item, and whenmethod,stval.opt,tol, ormax.iteris not valid (amax.iterbelow 1 or not a whole number removed the iteration limit). For anest_irtobject, supplyingdataorDis now an error instead of being ignored.est_score()now has a method forest_itemobjects, as its documentation already stated.irtfit()andsx2_fit()stop with a clear message whendatahas a number of columns other than the number of items or a response is not an integer within the categories of its item (such a response was counted in the wrong cells before), and they read character and factor responses as numbers instead of level codes.irtfit()also stops for ascoreof the wrong length, scores without variation, an invalidrange.score, and aloc.thetaother than"average"or"middle"(ignoring case; any other value was treated as"average"), leaves out examinees with a missing ability estimate instead of failing, and works when only one item remains after items with fewer than two responses are excluded.plot.irtfit()stops for atypeother than"both","icc", or"sr"and for anitem.locthat is not an evaluated item.sx2_fit()stops for a test with a single item, which has no S-X2 statistic. The warnings for excluded items and for missing responses insx2_fit()name the items, and the error for a response outside the categories names the item and its column.bisection()warns when the function values atlbandubhave the same sign, instead of returning a bound without notice, and it stops after exactlymax.ititerations instead ofmax.it + 1. Its help page names the last returned elementdelta, as in the code.est_score()withmethod = "INV.TCC"stops with a clear message when the test characteristic curve does not reach a sum score within theta from -20 to 20, for example for items with very small slopes or guessing parameters that sum to nearly the maximum score.ctt()computes alpha with each item removed faster for large tests.cac_lee()andcac_rud()leave out examinees with a missing ability estimate (or standard error) with a warning. Before,cac_lee()andcac_rud()stopped with an obscure error for a missing ability estimate, andcac_rud()returned wrong indices for a missing standard error. Both stop for cut scores that are not in strictly ascending order, andcac_rud()stops for a standard error that is not positive.cac_lee()no longer stops when a performance level contains no integer summed score, and it is much faster with ability estimates.panel_info()stops with a clear message for a route map that does not define a panel (a module without transitions, a cycle, or a module outside the stages) instead of running forever, and it finds every stage when the modules are not numbered in the order of the stages.panel_info(),run_mst(),reval_mst(), andfind_cut()read a logical route map as a 0/1 matrix.reval_mst()accepts item metadata with a missing guessing parameter, such as 2PLM items fromshape_df(). It checks and completes the item metadata asrun_mst()does, soitem.by.modanditem.by.pathhold plain data frames with upper-case model names and without parameter columns that are allNA.reval_mst()andrun_mst()stop with a clear message whencut_scoredoes not match the panel ormodulehas a number of columns other than the number of modules, they read aNULLelement ofcut_scorefor a stage with a single module as an empty vector, and they assign the cut scores to the modules of a stage in the order of the module indices.reval_mst()also stops with a clear message when a sum score has no inverse TCC estimate (withintpol = FALSEor arange.tccthat does not cover the estimates); before, it stopped with an obscure error or returnedNAfor every row ofeval.tb.run_mst()stops with a clear message whentolormax.iterinroute_scoreorfinal_scoreis not valid (amax.iterof 0 or 2.5 never ended), and it warns once about examinees without any observed response to the administered items.find_cut()no longer warns about two proper crossings when a crossing lies exactly on a grid point.ripd()results have aprint()method that prints the flagged items and the RIPD statistics of the analyses without and with purification;whatselects"no_purify"or"with_purify". Before,print()listed the whole object, including the ability estimates of every examinee.The
est_irtandest_itemmethods ofrdif()andcrdif()stop with a clear message whendataorDis passed, because both are taken from the object, and theest_itemmethods do so forscoreas well. Before, they were silently ignored or caused an obscure error.The purification of
rdif(),crdif(),ripd(),grdif(), andcatsib()is faster: after the first iteration, only the examinees who responded to the removed item are rescored whenncore = 1andmethodis a pattern scoring method. Results are unchanged.The purification of
rdif(),crdif(),ripd(),grdif(), andcatsib()gives the warning ofest_score()aboutncore > 1once instead of at every iteration.rdif(),crdif(), andripd()stop with a clear message formethod = "EAP.SUM"or"INV.TCC"when they estimate the abilities (score = NULLorpurify = TRUE), because these methods give no ability estimate for each response pattern. Before,rdif()andcrdif()stopped with an obscure message.ripd()andpcd2()also stop with a clear message whenmin.respis not a single nonnegative number, andripd()stops whenscoreis not numeric, whenalphais not a single number between 0 and 1 (analphaof 1 or more, or a text value, flagged every item), or whenmax.iteris not a positive whole number andpurify = TRUE. Theest_irtandest_itemmethods ofripd()stop with a clear message whendataorDis passed, and theest_itemmethod whenscoreis passed, as those ofrdif()andcrdif()do.pcd2()stops with a clear message whenmax.iteris not a positive whole number andpurify = TRUE, and whencrit.valis notNULLor a single nonnegative number. Before, it acceptedmax.iter = 2.5, and a textcrit.valflagged no item without a message.The
print()methods ofgrdif()andcatsib()leave the significance symbol of an item with a missing p-value blank, as those ofrdif()andcrdif()do for an item skipped withitem.skip. Before, it showed?, which the legend does not explain.The
print()methods ofrdif()andcrdif()leave the significance symbol of an item skipped withitem.skipblank. Before, it showed?, which the legend does not explain.The
dif_itemelementsrdifrandrdifsof theno_purifyresults ofrdif()are integer vectors likerdifrs, andn.iterof thewith_purifyresults ofrdif()andcrdif()is the integer 0 when no item is flagged in the initial analysis.npar.estofest_mg()andn.iterof thewith_purifyresults ofcatsib()are integers as well. The values are unchanged.When the Hessian of an item is singular during the estimation of
est_irt(),est_mg(), andest_item(), it is now repaired by adding a ridge to its diagonal instead of random numbers. The results are deterministic, and the random number stream of the user is no longer used by the estimation.The help pages of
est_irt(),est_mg(),est_item(), andgetirt()now describe how the standard errors are computed: the information of the item parameter priors is added to the cross-product information matrix ofest_irt()andest_mg(), andest_item()uses the observed information with the abilities treated as known. The help pages ofest_irt(),est_mg(), andgetirt()also state thattest.2checks whether the information matrix is singular, and those ofest_irt(),est_mg(), andest_item()state that the slope prioraprioris placed onDtimes the slope.est_irt()andest_mg()return a new last element,npar.detail, an integer vector with the numbers of free item parameters (item), of means and variances of the latent distributions (dist), and of weights of the empirical histograms (weight); their sum isnpar.est. Thesummary()output shows these three numbers under “Number of free parameters”, andgetirt()extracts it withwhat = "npar.detail". Objects created by earlier versions have nonpar.detail, and their summary shows the total only.Corrected and extended the help page of
est_irt(). The returnedloglikelihoodis the sum over examinees of the marginal log-likelihood of the response pattern, without the prior densities (it is not a sum over items);ncaseandposterior.distrefer to examinees, not response patterns;datais a matrix; and the returnedcallis listed. The help page also describes the response coding (0 to the number of categories minus 1), the role ofx,item.id,weights,group.mean, andgroup.varwith and without FIPC, the EM algorithm, the empirical histogram, the two FIPC methods (including that the returned distribution offipc.method = "OEM"is updated once afterward and that the standard errors ingroup.parfor FIPC are approximate), the counting of the free parameters, the bounds of the slopes, and the items without a maximum likelihood estimate.EtolandMaxEare described as the criterion and the maximum number of EM cycles, and the argument tables, the lists in the argument descriptions, and the examples were corrected. The same corrections apply togetirt()and to the arguments shared with other help pages.Corrected and extended the help pages of
est_mg(),est_item(), andgetirt()in the same way. The help page ofest_mg()now explainsfree.group, the roles ofweights,group.mean,group.var, andEmpHistfor groups in and not infree.group, the MG-FIPC algorithm (Kim & Kolen, 2016), and the structure of the returnedloglikelihood,group.par,posterior.dist,data,ncase, andnitem. The help page ofest_item()states that the estimates are Bayes modal when priors are used, that the items are estimated separately, and whatgroup.par,data,deleted.item, andn.responsecontain. TheREADMEand the reference of Kim and Kolen (2016) were corrected.The help pages of
est_irt()andest_mg()state that the EM cycles stop on the largest absolute change in the estimated item parameters (with all items fixed in FIPC, in the means and variances of the latent distributions). The estimated means and variances of the latent distributions and the weights of empirical histograms are not part of this criterion, so they can be less converged than the item parameters, and a smallerEtolgives more precise values; the defaultEtolofest_mg()(1e-3) is larger than that ofest_irt()(1e-4). The help pages also state that, withEmpHist = TRUE,group.parreports the fixed mean and variance of a rescaled histogram, which can differ slightly from the mean and variance of the returnedweights, and that the standard errors of the item parameters treat the latent distributions as known.Corrected the help pages of
bring.flexmirt()and the other import functions (the returned elements, the argumentstype,rePar, andrePar.gpc, the model labels, and the item types of mirt that are supported),write.flexmirt()(norm.popholds the mean and the variance, metadata from irtQ needsrePar = FALSE, and the scaling constant),run_flexmirt()(Windows only),summary()(the returned object),sx2_fit()(missing responses are replaced with 0), and the data setsLSAT6,simCAT_DC,simCAT_MX,simIPD,simMG, andsimMST. The package overview givesD = 1as the default and lists the functions for item parameter drift, MST panels, and the import and export of files. The help pages ofrdif(),crdif(),grdif(),catsib(),est_irt(),est_mg(), andest_item()state that thedigitsargument of their print methods is not used.The print methods are now documented on the help pages of the functions that create the objects (
rdif(),crdif(),grdif(),catsib(),irtfit(),run_mst(), andfind_cut()), and those of theest_irt(),est_mg(), andest_item()objects and their summaries on the help page ofsummary(), including the use of theirdigitsargument.
irtQ 1.3.1
CRAN release: 2026-10-08
Bug Fixes
Fixed the routing in
run_mst(). The ability estimate used to choose the next module was computed from the responses to the current module only. It is now computed from the responses to all modules administered so far, as intended. This applies to every routing method ("bmat","mfi", and cut scores) and everyroute_scoremethod, so simulated paths and results can differ from earlier versions, in particular from stage 3 onward.Fixed cut-score routing in
run_mst()for route maps in which a module reaches only some modules of the next stage. The examinee could be sent to a module that did not match the cut scores. Only the cut scores that separate the reachable modules are now used, which is the rule applied byreval_mst(). Results are unchanged when every module of a stage can be reached.Fixed the final estimate of
run_mst()for responses with missing values (responseargument). The ML, WL, MLF, MAP, and EAP estimates used the parameters of other items instead of those of the observed items.score_resp()andctt_distr()now match a data framekeyto the item columns by item number when itsitemcolumn is character or factor. Before, keys for tests with 10 or more items could be assigned to the wrong items.shape_df()withdefault.par = TRUEnow repeats a single value ofcatsormodelfor all items. Before, a singlecatsvalue gave item IDs of “V1” for every item and a guessing parameter of 0 instead of 0.2 for 3PLM items.irtfit()now computes the observed category proportions directly from the frequencies. Before, they came fromjanitor::adorn_percentages(), which can include the total column in the denominator in some environments and halve the proportions, the residuals, andoverSR.prop. Results are unchanged when the proportions were computed correctly.catsib()no longer overwritesscorewithsewhenseis given as a matrix or data frame.cac_rud()no longer stops with a dimnames error when a performance level has no examinees, and the label of the total row ofmarginalis now “marginal”, as incac_lee().simdat()treats NA values ing.drmas zeros, as the item metadata input does. Before, the responses to those items were all NA.The Wald confidence intervals in
plot.irtfit()now use the two-sided critical value,qnorm(1 - alpha / 2). Before, they usedqnorm(1 - alpha)and were 90% intervals at the defaultalpha = 0.05, while the Wilson intervals were 95% intervals.
Minor Improvements
reval_mst()now stops with an informative message when the modules in a stage differ in maximum sum score.In
run_mst()andreval_mst(), an ability estimate equal to a cut score is now routed to the higher module, as in the classification rule ofcac_lee()andcac_rud(). Results change only when an estimate equals a cut score exactly.ctt(),ctt_distr()(scored-category mode), and the CTT helper functions now stop with an informative error when an item score is not a whole number between 0 andcats - 1. Whencatsis inferred, every item has at least two categories, so an item that every examinee scores 0 on gets a difficulty of 0 and is flagged.ctt()reports the listwise deletion of incomplete rows once, and the item table has default row names.plot.find_cut()no longer passes an unusedinherit.aesargument togeom_vline(), which caused warnings with some ggplot2 versions.catsib()now stops with an informative message whenscoreis supplied withoutse. Before, it failed with a “missing value where TRUE/FALSE needed” error.
Documentation
Stated in
?reval_mstthat the recursion is based on inverse TCC estimates, that only inverse TCC scoring with cut-score routing is supported, and that modules in a stage must have the same maximum sum score; corrected the documented default ofthetaand the description of the returned list.In
?run_mst, described the cumulative routing estimate and the relation toreval_mst(), and stated that"EAP.SUM"and"INV.TCC"count a missing response as 0 in the final sum score.Corrected the interpretation of alpha with the item removed in
?ctt. Thectt()examples and the CTT article now use simulated IRT data.Corrected the description of the expected frequencies in
pcd2()and stated thatcrit.val = NULLflags no items.Reworded the motivation of
ripd().In
?simMST, noted that the item parameters are on the D = 1.702 scale. In?find_cut, clarified that the cut scores insimMSTwere obtained withfind_cut()and softened the statement about path reversals.Corrected the probability matrix of the first example in
?lwrc, the GPCM formula note in?irtQ, and the class ofprob.catsin?traceline. Corrected statements in the README, the vignette overview, and the articles (shrinkage of MAP and EAP,range.scoreinirtfit(),fix.idinest_mg(), the effect ofEmpHistin FIPC, the fixed-slope 1PLM, fixed guessing, the CATSIB regression correction, the purification procedure, and the usage notes of the utility functions). The DIF article now simulates item difficulties in a narrower range so that the pooled calibrations converge.
irtQ 1.3.0
CRAN release: 2026-10-05
New Features
New classical test theory (CTT) functions:
ctt()for item- and test-level statistics, withprint()andsummary()methods;freq_score()for a total-score frequency table;ctt_distr()for option or score-category distributions with point-biserial correlations; andscore_resp()for scoring raw selected-response data with an answer key. Item-total and option-total correlations are reported both with and without the item.Added a vignette, “Introduction to irtQ” (
vignette("irtQ")), that walks through calibration, scoring, and model-fit evaluation.
Bug Fixes
Fixed the EM convergence check in
est_irt()andest_mg(). It used the largest signed change instead of the largest absolute change, so the EM could stop before theEtolcriterion was met, most often when some parameters were held constant (for example, the guessing parameter of 1PLM and 2PLM items, or slopes fixed byfix.a.1plorfix.a.gpcm). Refitting a model can change the estimates and take more EM cycles. A fit from an earlier version that reportedmaxpar.diffequal to 0 most likely stopped early.est_irt()andest_mg()no longer warn that the convergence criteria are not satisfied whenfipc.method = "OEM"or when the EM converges at theMaxE-th cycle. In these cases,test.2can now report a possible local maximum.est_item()now uses Beta(5, 16) as the default prior for the guessing parameter, as inest_irt()andest_mg().getirt()now returnsposterior.distandscale.Dfor objects fromest_irt()andest_mg(), as documented.
Minor Improvements
find_cut()now usesD = 1by default, like the other functions in the package (previously 1.702). SupplyD = 1.702to reproduce earlier results.shape_df_fipc()now accepts a single value ofcatsormodelfor all new items.The mirt package moved from Imports to Suggests. Only
bring.mirt()uses it, and it now stops with an informative message when mirt is not installed.
Documentation
Added examples to
print.ctt(),summary.ctt(),print.summary.ctt(), and the data sets.Corrected the descriptions of
Etol,test.1,test.2, andmaxpar.diff, the return class ofest_mg(),dif_itemin the purified results ofrdif(),crdif(), andgrdif(), and the component names ofsimCAT_DCandsimCAT_MX.Corrected bibliographic details, added DOIs, cited the polytomous RDIF extensions in
rdif()andcrdif(), and listed every reference cited in the help pages on the package help page (?irtQ).
irtQ 1.2.0
CRAN release: 2026-07-27
New Features
Added a new function,
find_cut(), which identifies TIF-crossing routing cut scores for MST panels. For each adjacent module pair within a stage, the function locates the theta where the two modules’ test information functions (TIFs) intersect. A warning is issued when the mean difficulty order of modules within a stage differs from their input index order.Added a new S3 method,
plot.find_cut(), which visualizes TIF curves and routing cut scores stage by stage using ggplot2 facets. Proper, anomalous, and unselected cut scores are distinguished by line type. Alayoutargument ("vertical"/"horizontal") controls the facet orientation.Added a new function,
run_mst(), which simulates MST administrations for a given panel structure and returns response data along with ability and routing information for each simulated examinee.Added a new exported dataset,
simIPD, which contains simulated CAT response data for illustrating IPD detection withripd()andpcd2(). The dataset represents one replication of a CAT simulation (N = 3,000; test length = 30; 360-item 3PLM pool) in which 5% of items (18 items) had both and parameters decreased by 0.5.
Minor Improvements
est_irt(),est_item(), andest_mg()now accept a partialcontrollist. Users can specify only the arguments they wish to override (e.g.,control = list(iter.max = 500)); unspecified arguments fall back to their defaults viamodifyList().Reorganized the pkgdown reference page:
ripd()andpcd2()are now grouped under a new Item Parameter Drift (IPD) section, andreval_mst(),panel_info(),find_cut(),plot.find_cut(), andrun_mst()are grouped under a new Multistage-Adaptive Test (MST) section.
Documentation
Expanded the documentation for
ripd()by adding a@detailssection that covers the theoretical background of the RIPD framework, asymptotic distributions of the three RIPD statistics, a drift-type diagnostic guide, the CAT-specific three-step workflow, and the purification procedure. Also added a\donttest{}example demonstrating a complete CAT-based IPD detection workflow using thesimIPDdataset. Updated@referencesto include Lim & Choe (2023) and replaced the previous conference paper citation with the journal reference (Lim & Han, 2026).Added a
\donttest{}example topcd2()demonstrating CAT-based IPD detection using thesimIPDdataset, including the bootstrap critical value procedure described in Lim & Han (2026).Updated the MST Panel Evaluation and Simulation article (
vignettes/articles/mst-panel-evaluation.Rmd) to introducerun_mst()and extend the existingreval_mst()content with routing and scoring examples, afind_cut()-based principled cut score derivation, a side-by-side routing method comparison, and a Monte Carlo-vs-analytical validation example (Example 6).
Bug Fixes
- Rebuilt the
simMSTdataset: the previous version had 9 items duplicated across non-adjacent modules because the original assembly only enforced no-overlap within a single routing pathway. The new version enforces a global no-overlap constraint across all 7 modules (56 unique items total) and adds a mean(b) per-module band constraint; cut scores were regenerated viafind_cut()on the rebuilt modules.
irtQ 1.1.0
CRAN release: 2026-06-07
Major Improvements
- Improved the speed and reduced memory usage of item parameter estimation and standard error computation in
est_irt(),est_item(), andest_mg(). - Improved the computational speed of
est_score()by up to 52% for dichotomous items (N = 10,000) and up to 27% for mixed-format tests, through a series of optimizations. - Improved the computational speed of
sx2_fit()substantially by replacing the O(J^2) Lord-Wingersky recursion with a forward-backward pass (up to 11x faster for mixed-format tests with J = 55 items) and vectorizing internal helper functionsexpFreq(),obsFreq(), and the PRM category-collapsing routine.
New Features
- Added a unit test suite using the testthat 3rd edition (
testthat >= 3.0.0). Tests cover core functions includingdrm(),prm(),est_irt(),est_score(),est_mg(),rdif(),crdif(), andcatsib(), with the relevant tests across dichotomous, polytomous, and mixed-format item scenarios (355 tests total). - Added a new function,
ripd(), which implements the Residual-based Item Parameter Drift (RIPD) detection framework. The function computes three RIPD statistics: , , and (one for each item). captures uniform item parameter drift (IPD) via differences in mean raw residuals between groups, captures nonuniform IPD via differences in mean squared residuals, and is a combined chi-square-based statistic sensitive to both types of drift. An optional purification procedure is also supported.
New Articles
- Launched the irtQ documentation website at https://hwangQ.github.io/irtQ/, built with pkgdown. The site includes a full function reference index and the following vignettes covering the complete irtQ workflow:
- Getting Started with irtQ: an end-to-end overview of the package workflow.
-
Item Parameter Estimation: detailed guidance on
est_irt(),est_item(), andest_mg(). -
Ability Estimation: scoring methods available in
est_score(). -
Model-Data Fit Evaluation: using
irtfit()andsx2_fit()to assess model fit. -
DIF Detection: applying
rdif(),grdif(), andcatsib()to detect item bias. -
Classification Accuracy and Consistency: computing indices via
cac_lee()andcac_rud(). -
Utility Functions: usage of
info(),traceline(),lwrc(),simdat(), and related helpers. -
Evaluating MST Panels with
reval_mst(): measurement precision and bias evaluation for multistage adaptive tests.
Bug Fixes
- Fixed a minor bug in
sx2_fit()that caused incorrect cell collapsing between two adjacent score categories for polytomous items when computing the S- item fit statistic. - Fixed minor bugs in
est_score()andinfo(). - Resolved an issue in
catsib()where the function failed when all responses were missing (NA) in either the reference or focal group. - Updated
cac_rud()to include thexargument, allowing users to pass item metadata data frames directly. - Revised default
controlparameters inest_irt(),est_item(), andest_mg(), and updated the documentation accordingly. - Fixed a minor bug in
est_score()function in terms of Newton-Raphson method. - Fixed multiple stability issues in
catsib():- The final bin exclusion step in
catsib_item()used a hardcoded threshold of 3 instead of the user-suppliedmin.binsizeargument, causing inconsistent bin filtering behavior. - The reliability estimate
rho2incatsib_one()was not clamped to , so whenerrvar > sigma2(e.g., very few items or purification cascade), a negativerho2reversed the regression correction direction, inflating the Type I error rate. - When
errvar >= sigma2during purification,rho2collapsed to 0, causing all corrected scores to converge to the group mean. With group mean differences (impact), this produced empty bin data frames and an invalid purification result. A minimum floor of 0.05 is now enforced forrho2to preserve score spread.
- The final bin exclusion step in
- Fixed a critical bug in
covirt()where the guessing parameter (par[,3]) was incorrectly passed as the difficulty parameter (par[,2]) to theintegrand()function for DRM items. This caused the gradient computation to receivec = b, which zeroed out the and gradient components and produced a singular Fisher information matrix. Additionally,NAvalues inpar.3for 1PLM and 2PLM items are now substituted with 0 prior to gradient evaluation to preventNApropagation.
irtQ 1.0.0
CRAN release: 2025-07-17
The documentation for the
irtQpackage has been revised to reflect updates to function behavior, fix typos, and provide more relevant and detailed information for existing functions.A new function,
crdif(), has been added. This function computes three statistics from the residual-based DIF detection framework using categorical residuals (RDIF-CR). It allows for the detection of global DIF, particularly in polytomously scored items.A new function,
shape_df_fipc(), has been introduced. This function merges fixed-item metadata with automatically generated metadata for new items and produces a single data frame ordered by test position. It is designed to support fixed item parameter calibration (FIPC) via theest_irt()function.The
plot()method has been enhanced to support the display of all item characteristic curves for a given item in a single panel.The
pcd2()function has been updated to include a purification procedure.The
rdif()andcatsib()functions now include anitem.skipargument. This allows users to specify a numeric vector of item indices to exclude from the DIF analysis.
irtQ 0.2.1
CRAN release: 2024-08-25
Enhanced functionality of the
bind.fill()function by adding a new argumentfill. The value in the argument is used to fill in missing data when aligning datasets.Fixed a bug within the
est_irt()function that was previously unable to implement the fixed item parameter calibration (FIPC) when only freely estimating a single item given that all other items are fixed.Added a new function,
reval_mst(), which evaluates the measurement precision and bias in Multistage-Adaptive Test (MST) panels using a recursion-based evaluation method introduced by Lim et al. (2020).Added a new function,
pcd2(), which computes the Pseudo-count statistics (Cappaert et al., 2018; Stone, 2000) to detect item parameter drift.
irtQ 0.2.0
CRAN release: 2023-07-06
Introduced Warm’s (1989) Weighted Likelihood (WL) estimation method to the
est_score()function. This WL scoring method can now be utilized by settingmethod = "WL".Enhanced the speed of ability parameter estimation in the
est_score()function when using the ML, MLF, or MAP methods for themethodargument. The updated version performs approximately three times faster than its predecessor.Addressed a bug within the
est_score()function that was previously unable to accurately compute scores when only a single item data was provided. This issue was occurring with the EAP.SUM and INV.TCC estimation methods.-
Added two new functions for computing classification accuracy and consistency:
cac_rud()andcac_lee().-
cac_rud: This function implements Rudner’s (2001, 2005) method for computing classification accuracy and consistency. It takes cut scores, ability estimates, standard errors, and optional weights as inputs and returns a list containing a confusion matrix, marginal and conditional classification accuracy and consistency indices, the probability of being assigned to each level category, and the cut scores used in the analysis. -
cac_lee: This function implements Lee’s (2010) method for computing classification accuracy and consistency. It takes a data frame containing item metadata, cut scores, optional ability estimates, optional weights, a scaling factor, and a logical value indicating the cut score metric as inputs. It returns a list similar tocac_rud.
-
Added a new function,
llike_score(), which computes the loglikelihood of ability parameters given the item parameters and response data.Enhanced functionality of the
rdif()andgrdif()functions: Both now support the graded response model (GRM) and generalized partial credit model (GPCM).Fixed an issue in the
grdif()function that inaccurately calculated the GRDIF statistics when group membership was specified in a non-standard way. Specifically, the problem arose when 0 wasn’t used as the reference group and consecutive numbers (e.g., 1, 2, 3) weren’t used to represent focal groups in thegroupargument.
irtQ 0.1.1
CRAN release: 2023-05-08
Resolved the misalignment issue of standard errors in the output of the
est_irt()function whenfix.a.1pl = TRUEis specified and the items are calibrated using the 1PLM.Added a new function,
grdif(), to perform differential item functioning (DIF) analysis across multiple groups. This function calculates three generalized IRT residual DIF (GRDIF) statistics. For more information about the function and its usage, please refer to the accompanying documentation.Fixed several typos in the manual documentation
irtQ 0.1.0
CRAN release: 2023-01-10
Initial release on CRAN
The
irtQpackage is a successor of theirtplaypackage which was retracted from R CRAN due to the intellectual property (IP) violation. All issues of the IP violation have been clearly resolved in theirtQpackage.Most of the functions the
irtQpackage are identical in appearance and functionality to those ofirtplaypackage except a few functions (e.g.,shape_df(),est_score()). However, the computing speed of several functions (e.g.,est_irt(),est_score(),lwrc()) in theirtQpackage are faster than the previous ones in theirtplaypackage. Read the documentation carefully prior to using the functions.
