Package {scrutiny}


Title: Error Detection in Science
Version: 1.0.0
Maintainer: Lukas Jung <jung-lukas@gmx.net>
Description: Test published summary statistics for consistency (Brown and Heathers, 2017, <doi:10.1177/1948550616673876>; Allard, 2018, https://aurelienallard.netlify.app/post/anaytic-grimmer-possibility-standard-deviations/; Heathers and Brown, 2019, https://osf.io/5vb3u/). The package also provides infrastructure for implementing new error detection techniques.
License: MIT + file LICENSE
Encoding: UTF-8
LazyData: true
RoxygenNote: 7.3.3
Imports: cli (≥ 3.4.0), dplyr (≥ 1.1.0), ggplot2 (≥ 3.4.0), glue, grDevices, grid, lifecycle, methods, purrr (≥ 1.0.0), rlang (≥ 1.1.0), roundwork (≥ 0.2.0), stats, stringr, tibble, tidyr, tidyselect, vctrs, utils
Collate: 'aaa.R' 'bind-rows.R' 'function-factory-helpers.R' 'sd-binary.R' 'is-numeric-like.R' 'import-reexport.R' 'utils.R' 'debit.R' 'decimal-places.R' 'grim.R' 'function-map.R' 'add-up-to.R' 'add-up-to-map.R' 'mapper-function-helpers.R' 'audit-cols-minimal.R' 'audit.R' 'before-inside-parens.R' 'binomial.R' 'grimmer.R' 'grimmer-map.R' 'duplicate-tally.R' 'debit-map.R' 'restore-zeros.R' 'grim-stats.R' 'grim-map.R' 'data-doc.R' 'data-frame-predicates.R' 'grim-explain.R' 'debit-explain.R' 'seq-predicates.R' 'function-map-seq.R' 'debit-map-seq.R' 'disperse.R' 'function-map-total-n.R' 'debit-map-total-n.R' 'debit-plot-line.R' 'debit-plot.R' 'debit-values.R' 'duplicate-count-colpair.R' 'duplicate-count.R' 'exact-arithmetic.R' 'grimmer-explain.R' 'explain-map.R' 'explain-print.R' 'grim-granularity.R' 'grim-grid.R' 'grim-map-seq.R' 'grim-map-total-n.R' 'grim-plot-grid.R' 'grim-plot-line.R' 'grim-plot-rates.R' 'grim-values.R' 'grimmer-map-seq.R' 'grimmer-map-total-n.R' 'grimmer-plot-line.R' 'grimmer-sum-squares.R' 'grimmer-values.R' 'import-standalone-last-export.R' 'map-value-sets.R' 'method-audit-seq.R' 'method-audit-total-n.R' 'method-baseline-map.R' 'method-debit-map.R' 'method-dup-count-colpair.R' 'method-dup-count.R' 'method-grim-map.R' 'method-grimmer-map.R' 'method-tally.R' 'percentage-of.R' 'percentage-of-map.R' 'percentage-of-map-seq.R' 'reverse-map-seq.R' 'reverse-map-total-n.R' 'row-to-colnames.R' 'scrutiny-meta.R' 'scrutiny-package.R' 'seq-disperse.R' 'split-by-parens.R' 'write-doc-audit.R'
Suggests: ggrepel (≥ 0.9.3), knitr, pillar, rmarkdown, testthat (≥ 3.3.0)
Config/testthat/edition: 3
Config/testthat/parallel: true
Depends: R (≥ 4.3.0)
VignetteBuilder: knitr
URL: https://lhdjung.github.io/scrutiny/, https://github.com/lhdjung/scrutiny/
BugReports: https://github.com/lhdjung/scrutiny/issues
NeedsCompilation: no
Packaged: 2026-11-10 13:11:54 UTC; lukasjung
Author: Lukas Jung [aut, cre], Aurélien Allard [ctb], Nicolas Roman Posner [ctb]
Repository: CRAN
Date/Publication: 2026-11-11 00:10:02 UTC

scrutiny: Error Detection in Science

Description

logo

Test published summary statistics for consistency (Brown and Heathers, 2017, doi:10.1177/1948550616673876; Allard, 2018, https://aurelienallard.netlify.app/post/anaytic-grimmer-possibility-standard-deviations/; Heathers and Brown, 2019, https://osf.io/5vb3u/). The package also provides infrastructure for implementing new error detection techniques.

Author(s)

Maintainer: Lukas Jung jung-lukas@gmx.net

Other contributors:

See Also

Useful links:


Rename key columns according to the key arguments

Description

[Superseded]

absorb_key_args() is superseded by function_map(), whose mappers rename key columns themselves.

Call it in a handwritten mapper. If a key argument such as x names a column of data, it renames that column to the key argument's name, so that the rest of the mapper finds it.

Usage

absorb_key_args(data, reported, key_cols_call = NULL)

Arguments

data

Data frame passed to the mapper.

reported

String. Names of the key arguments.

key_cols_call

Optionally, a named character vector that maps key argument names to the columns of data that they refer to, such as c(x = "mean"). By default, it is read from the key arguments of the calling mapper.

Value

data, possibly with columns renamed. Reassign the result to data.

Examples

df <- tibble::tibble(mean = c(5.19, 4.56), n = c(28, 30))
absorb_key_args(df, c("x", "n"), key_cols_call = c(x = "mean"))

Do reported counts add up to their total?

Description

[Experimental]

add_up_to() checks whether reported subgroup sizes, or other counts, sum to the reported total.

The function is vectorized over cases, but add_up_to_map() is more convenient for testing many cases.

Usage

add_up_to(summands, total)

Arguments

summands

Integer. The counts of one case, or a list with one such vector per case.

total

Integer. The reported total, one per case.

Value

Logical. TRUE if the counts add up to total, FALSE if not, and NA if a value is missing, negative, or not a whole number, or if their sum or the total reaches 2^53, beyond which a sum is no longer exact.

Examples

add_up_to(c(40, 37, 43), total = 120)
add_up_to(c(40, 37, 44), total = 120)

# Several cases:
add_up_to(list(c(40, 37, 43), c(10, 12)), total = c(120, 23))

Test whether reported counts add up to their totals

Description

[Experimental]

add_up_to_map() applies add_up_to() to every row of a data frame.

Call audit() on the output for summary statistics.

Usage

add_up_to_map(data, summands, total = NULL, ...)

Arguments

data

Data frame with a total column and the columns to add up. Any other columns are returned alongside the results.

summands

Columns of data to add up, selected with tidyselect, such as starts_with("n_"). There is no default.

total

Optionally, the name of the column in data that contains the totals, if it isn't total.

...

These dots must be empty.

Value

A tibble with the summands columns, total, consistency, and any other columns from data, with the class scrutiny_add_up_to_map.

Summaries with audit()

audit() on add_up_to_map() output returns a tibble with one row and these columns:

  1. all_cases: total number of rows.

  2. incon_cases: number of inconsistent rows.

  3. emp_incon: proportion of inconsistent rows.

Examples

df <- tibble::tibble(
  n_control = c(40, 25, 31),
  n_treatment = c(37, 26, 30),
  n_waitlist = c(43, 24, 30),
  total = c(120, 75, 90)
)

df |>
  add_up_to_map(summands = starts_with("n_"))

Summarize scrutiny objects

Description

audit() summarizes the output of scrutiny functions that test data frames, such as grim_map(). It is an S3 generic with methods for the functions listed below. The columns of each summary are described in the documentation of the respective function.

On the output of a sequence or total-n mapper, such as grim_map_seq(), it returns one row per case; see audit-special.

Usage

audit(data, ..., .by = NULL)

Arguments

data

A data frame returned by one of the functions below.

...

These dots must be empty.

.by

Optionally, columns of data to summarize by, selected as in dplyr::select(), such as .by = study. Each group is summarized on its own, and its rows start with the grouping columns. Groups appear in the order of their first row. Use this rather than dplyr::group_by(), which removes the class that audit() dispatches on.

Value

A tibble with summary statistics.

Run before audit()

Function Class
grim_map() "scrutiny_grim_map"
grimmer_map() "scrutiny_grimmer_map"
debit_map() "scrutiny_debit_map"
percentage_of_map() "scrutiny_percentage_of_map"
add_up_to_map() "scrutiny_add_up_to_map"
⁠*_map_seq()⁠ functions "scrutiny_map_seq"
⁠*_map_total_n()⁠ functions "scrutiny_map_total_n"
duplicate_count() "scrutiny_dup_count"
duplicate_count_colpair() "scrutiny_dup_count_colpair"
duplicate_tally() "scrutiny_dup_tally"
audit() on seq output "scrutiny_audit_seq"
audit() on total-n output "scrutiny_audit_total_n"

Examples

pigs1 |>
  grim_map(digits_x = 2, items_averaged = 1, data_type = "integer") |>
  audit()

pigs4 |>
  duplicate_count() |>
  audit()

# Summaries by group:
pigs1 |>
  grim_map(digits_x = 2, items_averaged = 1, data_type = "integer") |>
  dplyr::mutate(study = rep(c("A", "B", "C"), each = 4)) |>
  audit(.by = study)

Classes of sequence and total-n mapper output

Description

audit() summarizes the output of functions made by function_map_seq(), such as grim_map_seq(), and of those made by function_map_total_n(), such as grim_map_total_n(), with one row per case. The columns of each summary are described in the documentation of the respective mapper.

Such output carries the generic class "scrutiny_map_seq" or "scrutiny_map_total_n", which audit() dispatches on, and a class for the particular mapper, listed below, so that you can write your own S3 methods for it. The summaries have the class "scrutiny_audit_seq" or "scrutiny_audit_total_n", which audit() summarizes further.

Sequence mappers

Function Class
grim_map_seq() "scrutiny_grim_map_seq"
grimmer_map_seq() "scrutiny_grimmer_map_seq"
debit_map_seq() "scrutiny_debit_map_seq"
percentage_of_map_seq() "scrutiny_percentage_of_map_seq"

Total-n mappers

Function Class
grim_map_total_n() "scrutiny_grim_map_total_n"
grimmer_map_total_n() "scrutiny_grimmer_map_total_n"
debit_map_total_n() "scrutiny_debit_map_total_n"

Examples

out <- pigs1 |>
  grim_map_seq(digits_x = 2, items_averaged = 1, data_type = "integer") |>
  audit()
out

# `audit()` summarizes the summary:
audit(out)

Compute minimal audit() summaries

Description

Call audit_cols_minimal() within an audit() method for the output of a consistency test mapper such as grim_map(). It returns a tibble with the three columns every such method must have:

  1. all_cases: the number of rows, including undecidable ones.

  2. incon_cases: the number of rows where consistency is FALSE. A case the test could not decide is NA there and not counted.

  3. emp_incon: the empirical rate of inconsistency, incon_cases / all_cases.

Add further columns as needed. See Examples for how to name the method.

Usage

audit_cols_minimal(data)

Arguments

data

Data frame returned by a mapper function.

Value

A tibble with the three columns above.

See Also

vignette("consistency-tests-in-depth").

Examples

# For a mapper `schlim_map()` that applies a test called SCHLIM and
# returns a data frame with the class `"scrutiny_schlim_map"`:
audit.scrutiny_schlim_map <- function(data, ...) {
  audit_cols_minimal(data)
}

Deprecated summaries of sequence and total-n mapper output

Description

[Deprecated]

audit_seq() and audit_total_n() were folded into audit() in scrutiny 1.0.0. It returns the same summaries for the output of sequence and total-n mappers, and also has .by.

Usage

audit_seq(data)

audit_total_n(data)

Arguments

data

Output of a sequence mapper, such as grim_map_seq(), or of a total-n mapper, such as grim_map_total_n().


Check that a mapper's input has the right column names

Description

[Superseded]

check_mapper_input_colnames() is superseded by function_map(), whose mappers check their input columns themselves.

Call it in a handwritten mapper. It errors if data lacks a key column, or if it already has a column named like the key result column ("consistency" by default).

Usage

check_mapper_input_colnames(
  data,
  reported,
  name_test,
  name_key_result = "consistency"
)

Arguments

data

Data frame. Input to the mapper.

reported

String. Names of the key columns that data must have, such as c("x", "n") for grim_map().

name_test

String (length 1). Plain-text name of the test, such as "GRIM", for the error message.

name_key_result

String (length 1). Name of the result column that the mapper adds. Default is "consistency".

Value

No return value; might throw an error.

See Also

vignette("consistency-tests-in-depth").


Is an object the output of a mapper function?

Description

Usage

is_map_df(x)

is_map_basic_df(x)

is_map_seq_df(x)

is_map_total_n_df(x)

Arguments

x

Object to be tested.

Details

See vignette("consistency-tests-in-depth") for the three tiers and the table at its end.

Value

Logical (length 1).

Examples

df1 <- pigs1 |>
  grim_map(digits_x = 2, items_averaged = 1, data_type = "integer")
df2 <- pigs1 |>
  grim_map_seq(digits_x = 2, items_averaged = 1, data_type = "integer")
df3 <- tibble::tribble(
  ~x1,  ~x2,  ~n,
  3.43, 5.28, 90,
  2.97, 4.42, 103
) |>
  grim_map_total_n(digits_x = 2, items_averaged = 1, data_type = "integer")

# All three are mapper output:
is_map_df(df1)
is_map_df(df2)
is_map_df(df3)

# Each is the output of one tier:
is_map_basic_df(df1)
is_map_seq_df(df2)
is_map_total_n_df(df3)

is_map_basic_df(df2)
is_map_seq_df(df3)

The DEBIT (descriptive binary) test

Description

debit() checks whether the reported mean and standard deviation of binary data are consistent with the reported sample size. With k ones among n values, the mean is k / n and the SD follows from k and n; the test asks whether any k produces both reported numbers at their decimal places.

The function is vectorized, but debit_map() is more convenient for testing many cases.

Usage

debit(
  x,
  sd,
  n,
  digits_x,
  digits_sd,
  data_type,
  sd_meaning = c("sd_sample", "sd_pop", "sd_sample_or_pop", "se"),
  formula = c("exact", "mean_n"),
  rounding = "up_or_down",
  threshold = 5,
  symmetric = FALSE
)

Arguments

x

Numeric. The reported mean of the binary data, between 0 and 1.

sd

Numeric. The reported standard deviation, between 0 and 1.

n

Integer. The reported sample size.

digits_x, digits_sd

Integer. The number of decimal places in x and sd, including trailing zeros. There are no defaults, since a numeric value does not keep them (1.40 is 1.4).

data_type

String. As in grim(): "integer" or "continuous". No default. For DEBIT, "integer" means the binary values 0 and 1 that the test assumes. Continuous data can have any mean and SD from 0 to 1, so the result is TRUE, but a single value can only have an SD of 0. Values outside of 0 to 1 are an error for either data type.

sd_meaning

String. What sd really is: the sample SD, "sd_sample" (the default); the population SD, "sd_pop"; either one, "sd_sample_or_pop"; or the standard error of the mean, "se". As in grimmer(), whose sections Defaults and SD meaning say why the sample SD is the default and when to choose another.

formula

String. "exact" (the default) only considers means that n binary values can have, i.e., k / n for a whole number k. "mean_n" treats the mean as continuous, as in Heathers and Brown (2019), and accepts some SDs that no binary sample of size n can have: debit(0.05, 0.21, 20, 2, 2, "integer", formula = "mean_n") is TRUE, although a mean of 0.05 with n = 20 means one 1 in 20, whose SD rounds to 0.22.

rounding

String. The rounding method assumed to have produced x and sd. Default is "up_or_down". See grim() for the others.

threshold, symmetric

The same as in grim().

Details

x, sd, n, digits_x, digits_sd, and data_type are vectorized and recycled to a common length by the tidyverse rules. All other arguments must have length 1.

x and sd are limited to about 15 significant digits, and x with n as in grim(). Within these limits, debit() decides value sets of any size exactly. With x missing, n must be below 2^52, about 4.5e15.

Value

Logical. TRUE if x, sd, and n are consistent, FALSE if not, and NA if the case cannot be decided: if n is not a positive whole number, or if a missing value could decide the case either way. See vignette("technical-notes"), section Missing values, on which cases a missing value leaves open. A sample SD divides by n - 1, so under sd_meaning = "sd_sample" (the default) and "se", n must also be at least 2.

References

Heathers, James A. J., and Brown, Nicholas J. L. 2019. DEBIT: A Simple Consistency Test For Binary Data. https://osf.io/5vb3u/.

See Also

debit_explain() to explain a result in plain words, debit_map() for data frames, and debit_values() and debit_plot_line() for the means and SDs behind a value set.

Examples

debit(
  x = 0.36,
  sd = 0.11,
  n = 20,
  digits_x = 2,
  digits_sd = 2,
  data_type = "integer"
)

Explain a DEBIT test step by step

Description

[Experimental]

debit_explain() tests a single mean and SD of binary data like debit() does, but also lays out the calculation in plain words: which numbers of ones the mean allows, and whether the SD of any of them would be reported as the given one.

The text is built from the numbers that decide the test itself, so it always agrees with debit().

Usage

debit_explain(
  x,
  sd,
  n,
  digits_x,
  digits_sd,
  data_type,
  sd_meaning = c("sd_sample", "sd_pop", "sd_sample_or_pop", "se"),
  formula = c("exact", "mean_n"),
  rounding = "up_or_down",
  threshold = 5,
  symmetric = FALSE
)

Arguments

x

Numeric. The reported mean of the binary data, between 0 and 1.

sd

Numeric. The reported standard deviation, between 0 and 1.

n

Integer. The reported sample size.

digits_x, digits_sd

Integer. The number of decimal places in x and sd, including trailing zeros. There are no defaults, since a numeric value does not keep them (1.40 is 1.4).

data_type

String. As in grim(): "integer" or "continuous". No default. For DEBIT, "integer" means the binary values 0 and 1 that the test assumes. Continuous data can have any mean and SD from 0 to 1, so the result is TRUE, but a single value can only have an SD of 0. Values outside of 0 to 1 are an error for either data type.

sd_meaning

String. What sd really is: the sample SD, "sd_sample" (the default); the population SD, "sd_pop"; either one, "sd_sample_or_pop"; or the standard error of the mean, "se". As in grimmer(), whose sections Defaults and SD meaning say why the sample SD is the default and when to choose another.

formula

String. Only "exact", the default, can be explained.

rounding

String. The rounding method assumed to have produced x and sd. Default is "up_or_down". See grim() for the others.

threshold, symmetric

The same as in grim().

Value

An object of class scrutiny_explain: a list with the result as consistency (TRUE, FALSE, or NA), the explanation's paragraphs, and the test and its inputs. It prints as the explanation.

Examples

debit_explain(
  x = 0.36,
  sd = 0.11,
  n = 20,
  digits_x = 2,
  digits_sd = 2,
  data_type = "integer"
)

debit_explain(
  x = 0.35,
  sd = 0.49,
  n = 20,
  digits_x = 2,
  digits_sd = 2,
  data_type = "integer"
)

DEBIT-test many cases at once

Description

debit_map() applies debit() to every row of a data frame with means, standard deviations, and sample sizes of binary data.

Call audit() on the output for summary statistics and debit_plot() to visualize it.

Usage

debit_map(
  data,
  digits_x,
  digits_sd,
  x = NULL,
  sd = NULL,
  n = NULL,
  ...,
  data_type,
  sd_meaning = c("sd_sample", "sd_pop", "sd_sample_or_pop", "se"),
  formula = c("exact", "mean_n"),
  rounding = "up_or_down",
  threshold = 5,
  symmetric = FALSE,
  show_rec = FALSE
)

Arguments

data

Data frame with columns x, sd, and n, and optionally data_type, digits_x, and digits_sd. Any other columns are returned alongside the results.

digits_x, digits_sd

Integer. The number of decimal places in x and sd, including trailing zeros. There are no defaults, since a numeric value does not keep them (1.40 is 1.4). Either a single number for the whole column or one number per row of data. If left out, they are taken from digits_x and digits_sd columns of data.

x, sd, n

Optionally, the names of the columns in data that contain these values, if they differ from the defaults.

...

These dots must be empty. Every argument after them must be named in full.

data_type

String. As in debit(). Give it as an argument or as a column of data; there is no default. It is returned as a column.

formula, rounding, threshold, symmetric, sd_meaning

Passed on to debit(), which explains the defaults.

show_rec

Logical. If TRUE, the reconstructed values are returned as further columns; see Value in debit_map(). Default is FALSE.

Value

A tibble with these columns:

If show_rec is TRUE, further columns right after consistency hold the reconstructed values: the bounds of sd and x (sd_lower, sd_upper, x_lower, x_upper) with sd_incl_lower and sd_incl_upper saying whether the SD bounds are inclusive; and sum_lower and sum_upper, the least and the greatest number of ones among n values whose mean would be reported as x. Under formula = "exact", a value set is consistent exactly if some k from sum_lower to sum_upper has an SD within the bounds of sd.

The tibble has the class scrutiny_debit_map, which is recognized by audit() and debit_plot().

Summaries with audit()

audit() on debit_map() output returns a tibble with one row and these columns:

  1. all_cases: total number of value sets.

  2. incon_cases: number of DEBIT-inconsistent value sets.

  3. emp_incon: proportion of DEBIT-inconsistent value sets.

  4. mean_x: mean of the x values.

  5. mean_sd: mean of the sd values.

  6. distinct_n: number of distinct n values.

References

Heathers, James A. J., and Brown, Nicholas J. L. 2019. DEBIT: A Simple Consistency Test For Binary Data. https://osf.io/5vb3u/.

See Also

explain_map() and explain_print() to explain each row's result in plain words.

Examples

# Call `debit_map()` on binary summary data such as these:
pigs3

# The `consistency` column shows whether the
# values to its left are DEBIT-consistent:
pigs3 |>
  debit_map(digits_x = 2, digits_sd = 2, data_type = "integer")

# Get test summaries with `audit()`:
pigs3 |>
  debit_map(digits_x = 2, digits_sd = 2, data_type = "integer") |>
  audit()

# Explain the results in plain words:
pigs3 |>
  debit_map(digits_x = 2, digits_sd = 2, data_type = "integer") |>
  explain_print(1:2)

DEBIT-test values around the reported ones

Description

debit_map_seq() varies the reported means, standard deviations, and sample sizes a few steps up and down and DEBIT-tests each resulting value set. This shows whether a small error in reporting or computing could explain an inconsistency.

Call audit() on the output for summary statistics.

Usage

debit_map_seq(
  data,
  digits_x,
  digits_sd,
  x = NULL,
  sd = NULL,
  n = NULL,
  ...,
  data_type,
  sd_meaning = c("sd_sample", "sd_pop", "sd_sample_or_pop", "se"),
  formula = c("exact", "mean_n"),
  rounding = "up_or_down",
  threshold = 5,
  symmetric = FALSE,
  show_rec = FALSE,
  var = c("x", "sd", "n"),
  dispersion = 1:5,
  out_min = "auto",
  out_max = "auto",
  include_reported = FALSE,
  include_consistent = FALSE
)

Arguments

data

A data frame that debit_map() could take.

digits_x, digits_sd

Integer. The number of decimal places in x and sd, including trailing zeros. There are no defaults, since a numeric value does not keep them (1.40 is 1.4). Either a single number for the whole column or one number per row of data. If left out, they are taken from digits_x and digits_sd columns of data. They also set the step sizes for dispersing x and sd.

x, sd, n

Optionally, the names of the columns in data that contain these values, if they differ from the defaults.

...

These dots must be empty. Every argument after them must be named in full.

data_type

String. As in debit(). Give it as an argument or as a column of data; there is no default. It is returned as a column.

formula, rounding, threshold, symmetric, sd_meaning

Passed on to debit(), which explains the defaults.

show_rec

Logical. If TRUE, the reconstructed values are returned as further columns; see Value in debit_map(). Default is FALSE.

var

String. The variables to disperse. Default is c("x", "sd", "n").

dispersion

Numeric. Steps up and down from the reported values, in units of the last decimal place. Default is 1:5.

out_min, out_max

Limits on the dispersed values. Both default to "auto", which keeps n at 2 or above (1 under sd_meaning = "sd_pop" or "sd_sample_or_pop") and x and sd between 0 and 1. A number applies to every dispersed variable, and NULL removes the limit.

include_reported

Logical. Should the reported values themselves be part of the sequences? Default is FALSE.

include_consistent

Logical. Should consistent value sets be dispersed as well, not just inconsistent ones? Default is FALSE.

Value

A tibble with the columns of debit_map() output plus diff_var, case, and var, as in grim_map_seq(). As there, the other columns of data are dropped.

Summaries with audit()

audit() on debit_map_seq() output returns a tibble with one row per case and these columns:

A count or distance is NA if it depends on a dispersed value set whose consistency is NA, on a variable whose reported value is missing, which is not dispersed, or on rows removed from the output, as by filter().

Call audit() again on that summary to summarize these columns further. na_count and na_rate there count the cases without a consistent value within the dispersion range, and those that a missing value leaves unknown.

Examples

# `debit_map_seq()` takes the same input as `debit_map()`:
pigs3

out <- pigs3 |>
  dplyr::slice(3:4) |>
  debit_map_seq(digits_x = 2, digits_sd = 2, data_type = "integer")
out

# Case-wise summaries:
out |>
  audit()

DEBIT-test with hypothetical group sizes

Description

Some studies report group means and standard deviations of binary data but only the total sample size, not the group sizes. For two groups, debit_map_total_n() tests hypothetical splits of the total, as grim_map_total_n() does.

Call audit() on the output for summary statistics.

Usage

debit_map_total_n(
  data,
  digits_x,
  digits_sd,
  x1 = NULL,
  x2 = NULL,
  sd1 = NULL,
  sd2 = NULL,
  n = NULL,
  ...,
  data_type,
  sd_meaning = c("sd_sample", "sd_pop", "sd_sample_or_pop", "se"),
  formula = c("exact", "mean_n"),
  rounding = "up_or_down",
  threshold = 5,
  symmetric = FALSE,
  show_rec = FALSE,
  dispersion = 0:5,
  n_min = NULL,
  n_max = NULL,
  constant = NULL
)

Arguments

data

Data frame with numeric columns x1, x2, sd1, sd2, and n: the two group means, the two group SDs, and the total sample size. Which group is 1 does not matter, since both pairings with the group sizes are tested, but x1 must belong with sd1 and x2 with sd2.

digits_x, digits_sd

Integer. The number of decimal places in the means and SDs, including trailing zeros. There are no defaults, since a numeric value does not keep them (1.40 is 1.4). Either a single number for both groups, or two numbers, one per group, which apply to every row of data. If left out, they are taken from digits_x and digits_sd columns of data, with one number per row for both groups. If the decimal places differ both by group and by row, call the function on each set of rows separately and audit() each output.

x1, x2, sd1, sd2, n

Optionally, the names of the columns in data that contain these values, if they differ from the defaults.

...

These dots must be empty. Every argument after them must be named in full.

data_type

String. As in debit(). Give it as an argument or as a column of data; there is no default. It is returned as a column.

formula, rounding, threshold, symmetric, sd_meaning

Passed on to debit(), which explains the defaults.

show_rec

Logical. If TRUE, the reconstructed values are returned as further columns; see Value in debit_map(). Default is FALSE.

dispersion

Numeric. Steps up and down from half the total n. Default is 0:5, i.e., the even split and five steps away from it.

n_min

Numeric. Minimal group size. Default is NULL: 2 under sd_meaning = "sd_sample" or "se", which divide by n - 1, and 1 under "sd_pop" or "sd_sample_or_pop".

n_max

Numeric. Maximal group size. Default is NULL, i.e., no maximum.

constant

Optionally, values that belong to the two groups, such as their labels: a length-2 vector for the groups of x1 and x2, or a list of such vectors. Each becomes a column, as in disperse_total(), and its values stay with their groups in both directions. Default is NULL.

Value

A tibble like that of grim_map_total_n(), with x and sd columns for the reported values, dir recording which group is paired with the larger group size (the equal split of an even total appears under "forth" only), the other columns of debit_map() output, and the other columns of data.

Summaries with audit()

audit() on debit_map_total_n() output returns a tibble with one row per case and these columns:

A count is NA if it depends on a scenario that could not be decided, or on rows removed from the output, as by filter().

Call audit() again on that summary to summarize these columns further.

References

Bauer, P. J., & Francis, G. (2021). Expression of Concern: Is It Light or Dark? Recalling Moral Behavior Changes Perception of Brightness. Psychological Science, 32(12), 2042–2043. https://journals.sagepub.com/doi/10.1177/09567976211058727

Heathers, James A. J., and Brown, Nicholas J. L. 2019. DEBIT: A Simple Consistency Test For Binary Data. https://osf.io/5vb3u/.

See Also

function_map_total_n(), which created this function from debit_map().

Examples

df <- tibble::tribble(
  ~x1,  ~x2,  ~sd1,  ~sd2,  ~n,
  0.30, 0.28, 0.17, 0.10, 70,
  0.41, 0.39, 0.09, 0.15, 65
)

debit_map_total_n(df, digits_x = 2, digits_sd = 2, data_type = "integer")

df |>
  debit_map_total_n(digits_x = 2, digits_sd = 2, data_type = "integer") |>
  audit()

Visualize DEBIT results

Description

debit_plot() plots the output of debit_map(). Each value set is a rectangle spanning the rounding bounds of its mean and SD, in blue if consistent and red if not, against the DEBIT line: the SD of binary data as a function of their mean, for the given sample size.

Usage

debit_plot(
  data,
  show_labels = TRUE,
  show_full_scale = TRUE,
  show_theme_other = TRUE
)

Arguments

data

Data frame returned by debit_map().

show_labels

Logical. Should the value sets be labeled with "mean; SD"? Default is TRUE.

show_full_scale

Logical. If TRUE, the x-axis spans the whole DEBIT line, from 0 to 1, regardless of the data. Default is TRUE.

show_theme_other

Logical. If TRUE, minor grid lines are removed and y-axis ticks are added. Default is TRUE.

Details

The line is that of the statistic debit_map() was called with: the sample SD by default, the population SD under sd_meaning = "sd_pop", both under "sd_sample_or_pop", or the SE under "se".

Under formula = "exact", the default of debit_map(), the means k / n that would be reported as each value set's x are marked as points on the line, and a value set is consistent if one of them lies inside its rectangle. If n is large, only the points that would not be drawn over by others are drawn, with a message.

If n varies in data, the lines for the smallest and largest n are drawn and the band between them is shaded; it contains the lines for all other n.

A rectangle can be too small to see, so each is drawn over a point at the reported mean and SD. Value sets with NA in a column needed for drawing are left out with a warning, and so are those of continuous data, to which DEBIT doesn't apply.

The labels come from ggrepel::geom_text_repel(), which places them at random to minimize overlap. A fixed seed makes the placement the same on every run; resizing the plot may give a better one.

Colors come from one scale for colour and fill, mapped to consistency. Add another to change them, as in the examples.

Value

A ggplot object.

References

Heathers, James A. J., and Brown, Nicholas J. L. 2019. DEBIT: A Simple Consistency Test For Binary Data. https://osf.io/5vb3u/.

Examples


pigs3 |>
  debit_map(digits_x = 2, digits_sd = 2, data_type = "integer") |>
  debit_plot()

# Other colors:
pigs3 |>
  debit_map(digits_x = 2, digits_sd = 2, data_type = "integer") |>
  debit_plot() +
  ggplot2::scale_colour_manual(
    values = c("TRUE" = "#2f7d74", "FALSE" = "#c1522e"),
    aesthetics = c("colour", "fill"),
    guide = "none"
  )


Visualize the standard deviations that DEBIT admits

Description

[Experimental]

debit_plot_line() draws the DEBIT test for a single value set, as grimmer_plot_line() does for GRIMMER. Of all SDs that could have been reported with digits_sd decimal places, each square is one that n binary values with mean x could have; the others are left out. The reported SD is marked above the row and labeled as consistent or inconsistent.

For many value sets, debit_plot() shows the results of debit_map(). debit_values() lists the unrounded means and SDs behind a reported pair.

Usage

debit_plot_line(
  x,
  sd,
  n,
  digits_x,
  digits_sd,
  data_type,
  sd_meaning = c("sd_sample", "sd_pop", "sd_sample_or_pop", "se"),
  formula = c("exact", "mean_n"),
  rounding = "up_or_down",
  threshold = 5,
  symmetric = FALSE,
  zoom = c("close", "medium", "wide"),
  color_con = "#0047ff",
  color_incon = "#ff2a00",
  color_attainable = "grey75"
)

Arguments

x, sd, n, digits_x, digits_sd, sd_meaning, formula, rounding, threshold, symmetric

The same as in debit(), but x, sd, n, digits_x, and digits_sd must have length 1.

data_type

String. "integer" if the values are 0 and 1, as in debit(). There is no default, so that every call states this assumption. "continuous" and NA are errors: if the data may be continuous, there are no values to draw.

zoom

String. How much of the row to show: "close" (the default), "medium", or "wide"; see Details.

color_con, color_incon

Strings. Color of the marker, the strip behind the squares, and the square at the reported value if it is consistent. color_con applies if the value is consistent, color_incon if not. Defaults are "#0047ff" and "#ff2a00".

color_attainable

String. Color of the squares for attainable values that were not reported, like the consistent cells of grim_plot_grid()'s raster. Default is "grey75".

Details

zoom works as in grim_plot_line(), but with zoom = "wide", the row runs from 0 to 1, the range of debit()'s inputs.

Under formula = "mean_n", the squares are the SDs of every mean that would be reported as x, whether or not n binary values can have it.

Value

A ggplot object.

Examples

# A mean of 0.35 with an SD of 0.49 is consistent with 20 binary values,
# but 0.41 is not:
debit_plot_line(
  x = 0.35,
  sd = 0.49,
  n = 20,
  digits_x = 2,
  digits_sd = 2,
  data_type = "integer"
)
debit_plot_line(
  x = 0.35,
  sd = 0.41,
  n = 20,
  digits_x = 2,
  digits_sd = 2,
  data_type = "integer"
)

Achievable means and SDs of binary data

Description

[Experimental]

debit_values() returns every pair of a mean and an SD that n binary values (0 and 1) can have and that would be reported as x and sd. It is the counterpart of grim_values() and grimmer_values() for debit(), which is TRUE exactly if there is at least one such pair.

debit_closest() returns the single achievable pair whose SD is closest to sd, like grimmer_closest(). If x and sd are consistent, it is one of the pairs that debit_values() returns. Otherwise, it is closest among the pairs whose mean would be reported as x, or, if there are none, it is the pair whose mean grim_closest() returns.

Usage

debit_values(
  x,
  sd,
  n,
  digits_x,
  digits_sd,
  data_type,
  sd_meaning = c("sd_sample", "sd_pop", "sd_sample_or_pop", "se"),
  formula = c("exact", "mean_n"),
  rounding = "up_or_down",
  threshold = 5,
  symmetric = FALSE
)

debit_closest(
  x,
  sd,
  n,
  digits_x,
  digits_sd,
  data_type,
  sd_meaning = c("sd_sample", "sd_pop", "sd_sample_or_pop", "se"),
  formula = c("exact", "mean_n"),
  rounding = "up_or_down",
  threshold = 5,
  symmetric = FALSE
)

Arguments

x

Numeric. The reported mean of the binary data, between 0 and 1.

sd

Numeric. The reported standard deviation, between 0 and 1.

n

Integer. The reported sample size.

digits_x, digits_sd

Integer. The number of decimal places in x and sd, including trailing zeros. There are no defaults, since a numeric value does not keep them (1.40 is 1.4).

data_type

String. "integer" if the values are 0 and 1, as in debit(). There is no default, so that every call states this assumption. "continuous" is an error: such data can have any value that would be reported as the one given, so there are none to list. Under NA, a value set is undecidable unless no data can have it.

sd_meaning

String. As in debit(), but "sd_sample_or_pop" is not allowed: the same data have a different SD under each premise, so call debit_values() once for each. Under "se", the sd column holds the standard error.

formula

String. As in debit(), but only "exact", the default, is allowed: "mean_n" takes the mean as anything that would be reported as x, so there are no values to list.

rounding

String. The rounding method assumed to have produced x and sd. Default is "up_or_down". See grim() for the others.

threshold, symmetric

The same as in grim().

Details

More than a million numbers of ones whose mean would be reported as x is an error, since they are listed before the SD narrows them down. That takes an n of more than about 10^(digits_x + 6).

In debit_closest(), pairs whose SDs are equally close go by the mean closer to x, then by the smaller number of ones.

Value

A tibble with one row per achievable pair. case is the index of the value set, as in grim_values(). x and sd are the achievable values, n the reported sample size, and sum the number of ones, k in the mean k / n. An inconsistent value set has no rows, and one that cannot be decided has one row of NA but for n, as in debit().

debit_closest() returns the same columns with exactly one row per value set: of NA but for n if a value is missing or the value set cannot be decided.

See Also

debit(), debit_map(), debit_plot_line(), and sd_binary_groups().

Examples

# A mean of 0.35 and an SD of 0.49 are consistent with 20 binary values,
# 7 of them ones:
debit_values(
  x = 0.35,
  sd = 0.49,
  n = 20,
  digits_x = 2,
  digits_sd = 2,
  data_type = "integer"
)

# None if the value set is inconsistent:
debit_values(
  x = 0.35,
  sd = 0.41,
  n = 20,
  digits_x = 2,
  digits_sd = 2,
  data_type = "integer"
)

# With few decimal places and a large sample, there can be several:
debit_values(
  x = 0.5,
  sd = 0.5,
  n = 300,
  digits_x = 1,
  digits_sd = 1,
  data_type = "integer"
)

# The closest pair to an inconsistent one:
debit_closest(
  x = 0.35,
  sd = 0.41,
  n = 20,
  digits_x = 2,
  digits_sd = 2,
  data_type = "integer"
)

Count decimal places

Description

decimal_places() counts the decimal places of numbers, or of numbers written as strings.

Usage

decimal_places(x, sep = ".")

Arguments

x

Numeric, or strings that contain numbers.

sep

String. The separator between the integer part and the decimal places, matched literally. Default is ".". Only applies to strings; numbers are always written with a point.

Details

The function counts the run of digits after the first sep, after trimming whitespace and applying any exponent: "5.30%" has two decimal places, "1e-5" has five, and "1..5" has none. Missing and non-finite values have NA decimal places, as do the string "NA", a blank string, and strings that as.numeric() reads as an infinity or NaN.

Numeric values with more than 15 significant digits cannot be counted accurately; enter such numbers as strings.

Value

Integer. The number of decimal places of each element of x.

Trailing zeros

A numeric value has lost its trailing zeros: 3.70 is stored as 3.7 and has one decimal place. If trailing zeros matter, count them while the values are still strings, such as "3.70", and give the counts to the tests as digits_x or digits_sd, as in dplyr::mutate(data, digits_x = decimal_places(x), x = as.numeric(x)).

Examples

decimal_places(x = 2.851)
decimal_places(x = "2.851")

# Trailing zeros are only counted in strings:
decimal_places(x = c(7.3900, "7.3900"))

# Whitespace at the end of a string is ignored:
decimal_places(x = "6.0     ")

Count decimal places in a data frame

Description

[Deprecated]

decimal_places_df() is deprecated because it replaces the values by their counts, while the tests need both. Use decimal_places() in dplyr::mutate() instead, as in mutate(digits_x = decimal_places(x)).

decimal_places_df() replaces every value in the selected columns by its number of decimal places. By default, it operates on all columns coercible to numeric.

Usage

decimal_places_df(
  data,
  cols = everything(),
  check_numeric_like = TRUE,
  sep = "."
)

Arguments

data

Data frame.

cols

Columns to operate on, selected with tidyselect. Default is everything(), restricted by check_numeric_like.

check_numeric_like

Logical. If TRUE (the default), only columns that is_numeric_like() accepts are processed. A column that isn't numeric-like gets a warning if cols is given and selects it, or if it is processed anyway, unless it is empty.

sep

String. The separator between the integer part and the decimal places, matched literally. Default is ".".

Value

Data frame with the selected columns replaced by their counts of decimal places.

See Also

decimal_places().

Examples

iris <- iris |>
  tibble::as_tibble() |>
  dplyr::mutate(across(everything(), as.character))

# All numeric-like columns, silently leaving out `Species`:
iris |>
  decimal_places_df()

# Selected columns only:
iris |>
  decimal_places_df(cols = starts_with("Sepal"))

Vary hypothetical group sizes

Description

If a study reports a total sample size but no group sizes, these functions generate pairs of hypothetical group sizes that add up to the total:

They are the basis of grim_map_total_n() and the other functions made by function_map_total_n().

Usage

disperse(n, dispersion = 0:5, n_min = 1L, n_max = NULL, constant = NULL)

disperse2(n, dispersion = 0:5, n_min = 1L, n_max = NULL, constant = NULL)

disperse_total(n, dispersion = 0:5, n_min = 1L, n_max = NULL, constant = NULL)

Arguments

n

Numeric. In disperse(), half of an even total. In disperse2(), the two consecutive numbers closest to half of an odd total, such as c(25, 26) for 51. In disperse_total(), the total itself.

dispersion

Numeric. Non-negative whole numbers of steps up and down from n (or, in disperse_total(), from half of n). Default is 0:5.

n_min

Numeric. Minimal group size. Default is 1L.

n_max

Numeric. Maximal group size. Default is NULL, no maximum.

constant

Optionally, a length-2 vector, or a list of such vectors (which includes a data frame with two rows), to repeat alongside each pair of group sizes. Default is NULL.

Details

A pair with a group size below n_min or above n_max is dropped. Columns from constant are named after a named list's elements, or "constant1", "constant2", etc. for an unnamed one; a single pair is "constant".

Value

A tibble with these columns:

References

Bauer, P. J., & Francis, G. (2021). Expression of Concern: Is It Light or Dark? Recalling Moral Behavior Changes Perception of Brightness. Psychological Science, 32(12), 2042–2043. https://journals.sagepub.com/doi/10.1177/09567976211058727

See Also

function_map_total_n() and grim_map_total_n().

Examples

# For a total of 40, `n` is 20:
disperse(n = 20)

disperse(n = 20, dispersion = c(3, 6, 10))

# For an odd total, `disperse2()` takes two group sizes:
disperse2(n = c(25, 26))

# `disperse_total()` takes the total itself:
disperse_total(n = 40)
disperse_total(n = 51)

# Values that repeat alongside the pairs:
disperse_total(37, constant = c("5.24", "3.80"))

df_constant <- tibble::tibble(
  name = c("Paul", "Mathilda"), age = 27:28,
  registered = c(TRUE, FALSE)
)
disperse_total(37, constant = df_constant)

Count duplicate values

Description

duplicate_count() returns a frequency table of the values in a vector or data frame. For a data frame, it pools the values of all columns and records which columns each value appears in.

This is a blunt tool for initial checks. If many values have few characters each, duplicates occur by chance and mean little.

Call audit() on the output for summary statistics.

Usage

duplicate_count(x, ignore = NULL, locations_type = c("character", "list"))

Arguments

x

Vector, matrix, or data frame. Missing values are not counted, nor are blank strings, "NA", and "NaN", which count as missing.

ignore

Optionally, a vector of values not to count.

locations_type

String. "character" (the default) pastes the column names in locations into one string; "list" keeps them as a vector per value, which is better for programming.

Value

A tibble with these columns, or only the first two if x is not named:

The tibble has the class scrutiny_dup_count, which is recognized by audit().

Summaries with audit()

audit() on duplicate_count() output returns summary statistics for frequency and locations_n (or frequency alone if x is not named).

See Also

Examples

iris |>
  duplicate_count()

iris |>
  duplicate_count() |>
  audit()

# Ignore some values:
iris |>
  duplicate_count(ignore = c("setosa", "versicolor", "virginica"))

Count duplicate values by column pair

Description

duplicate_count_colpair() counts, for every pair of columns in a data frame, the values that occur in both. Results are ordered by that count, in descending order.

Usage

duplicate_count_colpair(data, ignore = NULL, show_rates = TRUE)

Arguments

data

Data frame or matrix with at least two columns. Blank strings, "NA", and "NaN" count as missing.

ignore

Optionally, a vector of values not to count.

show_rates

Logical. If TRUE (the default), add total_x, total_y, rate_x, and rate_y; see Value. FALSE is faster.

Value

A tibble with these columns:

Summaries with audit()

audit() on duplicate_count_colpair() output returns summary statistics for the numeric columns.

See Also

Examples

mtcars |>
  duplicate_count_colpair()

mtcars |>
  duplicate_count_colpair() |>
  audit()

Count duplicates at each observation

Description

For every value in a vector or data frame, duplicate_tally() counts how often it occurs in the whole input, and reports the count next to the value.

Call audit() on the output for summary statistics.

Usage

duplicate_tally(x, ignore = NULL, colname_end = "n")

Arguments

x

Vector, matrix, or data frame. Blank strings, "NA", and "NaN" count as missing.

ignore

Optionally, a vector of values not to check. Their tally is NA, as is that of missing values.

colname_end

String. Suffix of the tally columns. Default is "n".

Details

Values with few characters are often duplicated by chance: in iris, 99% of values have a duplicate. Duplicates are more meaningful when there are fewer values and they have more characters.

Values are compared and returned as strings, so 1 and "1" are duplicates, but 2 and "2.0" are not: 2.0 becomes "2".

Value

A tibble with the columns of x (a value column if x is a vector), each followed by its tally column. It has the class scrutiny_dup_tally, which is recognized by audit().

Summaries with audit()

audit() on duplicate_tally() output returns summary statistics for each tally column, with a .total row across all of them. It finds these columns by the names that duplicate_tally() gave them, so a renamed one is left out.

See Also

Examples

duplicate_tally(x = pigs4)

duplicate_tally(x = pigs4$snout)

pigs4 |>
  duplicate_tally() |>
  audit()

pigs4 |>
  duplicate_tally(ignore = c(8.13, 7.57))

Explain mapper results row by row

Description

[Experimental]

explain_map() takes the output of grim_map(), grimmer_map(), or debit_map() and adds an explanation column: the calculation behind each row's result in plain words, as printed by grim_explain(), grimmer_explain(), and debit_explain() for a single case. It also takes the output of the sequence and total-n mappers built on them, such as grim_map_seq().

To read the explanations, use explain_print().

Usage

explain_map(data, ...)

Arguments

data

Data frame returned by one of the mappers named above.

...

Must be empty for scrutiny's methods; methods for other classes may use them.

Details

Each row is tested again from its own values and the settings of the mapper call, such as rounding and x_meaning. If the result differs from the row's consistency value, the row must have been changed after testing, and explain_map() throws an error.

DEBIT explanations cover formula = "exact" only.

Value

data with an explanation column, a string per row.

Examples

pigs1 |>
  grim_map(digits_x = 2, items_averaged = 1, data_type = "integer") |>
  dplyr::filter(!consistency) |>
  explain_map()

Print the explanations of mapper results

Description

[Experimental]

explain_print() prints the explanations behind the results of a mapper such as grim_map(), one row after the other. It calls explain_map() on the printed rows only.

Usage

explain_print(data, ...)

Arguments

data

Data frame returned by grim_map(), grimmer_map(), or debit_map(), or by their sequence and total-n mappers.

...

Optionally, the rows to print, as in dplyr::slice(): positive integers to keep rows, negative ones to drop them. Default is to print all rows.

Value

data, invisibly.

Examples

pigs1 |>
  grim_map(digits_x = 2, items_averaged = 1, data_type = "integer") |>
  explain_print(1:2)

# Inconsistent rows only:
pigs1 |>
  grim_map(digits_x = 2, items_averaged = 1, data_type = "integer") |>
  dplyr::filter(!consistency) |>
  explain_print()

Create new ⁠*_map()⁠ functions

Description

function_map() creates a basic mapper: a function that applies a single-case test such as grim_scalar() to every row of a data frame. grim_map(), grimmer_map(), and debit_map() are made by it.

See vignette("consistency-tests-in-depth"), section Creating basic mappers with function_map().

function_map() is a developer tool: it may change in a minor release, with a NEWS entry.

Usage

function_map(
  .fn,
  .reported = NULL,
  .name_test,
  .name_key_result = "consistency",
  .name_class = NULL,
  .args_disabled = NULL,
  .args_by_row = NULL,
  .args_fixed = NULL,
  .cols_helper = NULL,
  .col_names = NULL,
  .cols_derived = NULL,
  .reported_variadic = NULL,
  ...
)

Arguments

.fn

Single-case function to apply to each row. It must return a single logical value or, if .col_names is specified, a list of values with the key result first.

.reported

String. Names of the arguments of .fn that take the reported values, such as c("x", "n"). These become the key columns. May be NULL if .reported_variadic is specified. Key and helper columns named n, items_averaged, or absolute are taken to be whole numbers, as are the decimal counts of .args_by_row: a value within floating-point noise of one, such as 100 * 0.29, is rounded to it before .fn sees it.

.name_test

String (length 1). Plain-text name of the test, such as "GRIM".

.name_key_result

(Experimental) String (length 1). Name of the key result column. Default is "consistency".

.name_class

String. Optionally, one or more classes to add to the output. Default is NULL; see Details for the class that is always added.

.args_disabled

Optionally, names of arguments of .fn that the factory-made function should not have. Specifying one of them is an error.

.args_by_row

Optionally, names of arguments of .fn that may vary by row, such as "digits_x". The factory-made function takes a single value or one per row of data for each. If one is left out, it is read from a column of data by that name, if there is one. They are returned as columns. One whose name starts with digits_ is a decimal count, returned as integer like digits_x.

.args_fixed

Optionally, a named list of values that are always passed to .fn, such as list(show_reason = TRUE). The factory-made function has no arguments for them; specifying one is an error.

.cols_helper

Optionally, names of arguments of .fn that may also be given as columns of data, such as "items_averaged". A helper column is returned after the key columns if it was given, as a column or as an argument. Giving both is an error if they disagree.

.col_names

Optionally, names of the columns that a list returned by .fn unpacks into, as with show_rec = TRUE. The first name must be .name_key_result. If data has no rows, the columns are those that .fn returns for a row of missing values, or, if it fails on one, only the key result column.

.cols_derived

Optionally, a named list of vectorized functions that compute further columns from the same input, such as list(exp_incon = grim_exp_incon). Each is called once, with the whole columns and only the arguments it has formals for, and must return one value per row; a .reported_variadic argument comes as a list of each row's values. The columns follow the key result column.

.reported_variadic

Optionally, a single string naming an argument of .fn that takes the values of one row as a vector. The factory-made function gets an argument by that name, without a default, that selects any number of columns with tidyselect syntax. See Details.

...

These dots must be empty.

Details

.reported_variadic is for a test whose number of key columns depends on the data, such as whether the values in any number of columns add up to the value in another one. .fn takes those values as a single vector, and the factory-made function selects the columns with tidyselect; see Examples. The selection must not be empty and must not include a column that already has a role in the test. The selected columns are returned as themselves. Such a mapper cannot be wrapped by function_map_seq() or function_map_total_n(), which derive their arguments from .reported.

The output has the class "scrutiny_{tolower(.name_test)}_map", with spaces as underscores, e.g., "scrutiny_grim_map", in addition to any .name_class. The arguments that applied to the whole call, such as rounding, are recorded on the output; see scrutiny_meta().

Value

A function with these arguments:

Value returned by the factory-made function

A tibble with the .reported_variadic columns, the .reported columns, the helper columns that were given, the .args_by_row columns, and the key result column (consistency by default), which is TRUE for consistent value sets, FALSE for inconsistent ones, and NA if the test can't decide. Any .cols_derived columns, any .col_names columns, and the other columns of data follow, in that order.

Examples

# A single-case function for SCHLIM, a mock test:
schlim_scalar <- function(y, n) {
  (y / 3) > n
}

# The mapper:
schlim_map <- function_map(
  .fn = schlim_scalar,
  .reported = c("y", "n"),
  .name_test = "SCHLIM"
)

df1 <- tibble::tibble(y = 16:25, n = 3:12)
schlim_map(df1)

# A test over any number of columns: do the parts add up to the total?
# The single-case function takes the parts as one vector...
sum_check_scalar <- function(parts, total, tolerance = 0) {
  abs(sum(parts) - total) <= tolerance
}

# ...which `.reported_variadic` names:
sum_check_map <- function_map(
  .fn = sum_check_scalar,
  .reported = "total",
  .reported_variadic = "parts",
  .name_test = "SUMCHECK"
)

df2 <- tibble::tibble(
  item_1 = c(10, 20, 30),
  item_2 = c(5, 5, 5),
  item_3 = c(1, 2, 3),
  total  = c(16, 27, 40)
)

# The `parts` argument selects columns with tidyselect syntax:
sum_check_map(df2, parts = starts_with("item"))

Create new ⁠*_map_seq()⁠ functions

Description

function_map_seq() creates a sequence mapper from a basic mapper such as grim_map(): a function that varies the reported values a few steps up and down and tests each resulting value set, as grim_map_seq() does. By default, only inconsistent value sets are dispersed.

See vignette("consistency-tests-in-depth"), section Sequence mapper.

function_map_seq() is a developer tool: it may change in a minor release, with a NEWS entry.

Usage

function_map_seq(
  .fn,
  .var = .reported,
  .reported,
  .name_test,
  .name_key_result = "consistency",
  .name_class = NULL,
  .args_disabled = NULL,
  .dispersion = 1:5,
  .out_min = "auto",
  .out_max = "auto",
  .var_bounds = NULL,
  .include_reported = FALSE,
  .include_consistent = FALSE,
  ...
)

Arguments

.fn

A basic mapper function such as grim_map(), typically made by function_map(). It must return its input data frame with a logical column named by .name_key_result added.

.var

String. The variables that the factory-made function disperses by default. Defaults to .reported.

.reported

String. All variables the factory-made function can disperse.

.name_test

String (length 1). Plain-text name of the test, such as "GRIM", for messages and the output's class.

.name_key_result

(Experimental) String (length 1). Name of the key result column. Default is "consistency". Must be the value that .fn was created with.

.name_class

String. Optionally, one or more classes to add to the output. Default is NULL.

.args_disabled

String. Optionally, names of arguments of .fn that the factory-made function should reject.

.dispersion

Numeric. Steps up and down from the reported values, in units of the last decimal place. Default is 1:5.

.out_min, .out_max

Limits on the dispersed values. A number applies to every dispersed variable, and NULL removes the limit. Default is "auto", which takes the limit from .var_bounds.

.var_bounds

Named list, or NULL (the default). Each element is named after a variable in .reported and is a numeric vector of its least and greatest possible value, with NA for an unbounded side, such as list(n = c(1, NA), sd = c(0, NA)). A variable not named here is unbounded, except for n, which always has a minimum of 1. For a bound that depends on a setting, an element may also be a function that takes the settings .fn recorded for its call, as a named list such as list(sd_meaning = "sd_sample"), and returns such a vector. The bounds only keep impossible values, such as a sample size of 0, from being tested. They have no bearing on consistency.

.include_reported

Logical. Should the reported values themselves be part of the sequences? Default is FALSE.

.include_consistent

Logical. Should consistent value sets be dispersed as well? Default is FALSE.

...

These dots must be empty.

Details

The arguments of function_map_seq() set the defaults of the corresponding arguments of the factory-made function, which can be overridden in each call.

Each variable in var is dispersed around its own reported value while the others stay as reported.

To export a factory-made function from another package, follow the purrr FAQ on adverbs; see vignette("consistency-tests-in-depth"), section Context and export.

Value

A function such as these:

Factory-made function Dispersed variables Test vignette
grim_map_seq() "x", "n" vignette("grim-in-depth")
grimmer_map_seq() "x", "sd", "n" vignette("grimmer-in-depth")
debit_map_seq() "x", "sd", "n" vignette("debit")

The settings of .fn, i.e., its arguments after ..., such as rounding, are arguments of this function as well, with the same defaults. So are its helper arguments, such as items_averaged. Its ... pass any other arguments on to .fn, which is only of use for a .fn without ... of its own.

Conventions

Name the factory-made function after .fn with ⁠_seq⁠ appended, as in grim_map_seq() from grim_map(). The output has the classes "scrutiny_map_seq" and "scrutiny_{tolower(.name_test)}_map_seq". Prefix any class you add via .name_class with your package's name, as in "mypkg_schlim_map_seq".

Examples

# Function definition of `grim_map_seq()`:
grim_map_seq <- function_map_seq(
  .fn = grim_map,
  .reported = c("x", "n"),
  .name_test = "GRIM"
)

Create new ⁠*_map_total_n()⁠ functions

Description

function_map_total_n() creates a total-n mapper from a basic mapper such as grim_map(): a function for two groups whose statistics are reported with the total sample size but without the group sizes, as grim_map_total_n() is. It uses disperse_total() to create pairs of hypothetical group sizes that add up to the total, and tests the reported statistics against each pair.

See vignette("consistency-tests-in-depth"), section Total-n mapper.

function_map_total_n() is a developer tool: it may change in a minor release, with a NEWS entry.

Usage

function_map_total_n(
  .fn,
  .reported,
  .name_test,
  .name_key_result = "consistency",
  .name_class = NULL,
  .dispersion = 0:5,
  .n_min = 1L,
  .n_max = NULL,
  .constant = NULL,
  ...
)

Arguments

.fn

A basic mapper function such as grim_map(), typically made by function_map(). It must return its input data frame with a logical column named by .name_key_result added.

.reported

String. Names of the group-specific statistics reported alongside the total, such as "x" for GRIM and c("x", "sd") for DEBIT. In the input of the factory-made function, each appears as two columns with the suffixes 1 and 2, such as x1 and x2.

.name_test

String (length 1). Plain-text name of the test, such as "GRIM", for messages and the output's class.

.name_key_result

(Experimental) String (length 1). Name of the key result column. Default is "consistency". Must be the value that .fn was created with.

.name_class

String. Optionally, one or more classes to add to the output. Default is NULL.

.dispersion, .n_min, .n_max, .constant

Passed on to disperse_total(), with the defaults from there. .n_min may also be a function of the settings of .fn, as a named list, that returns the default n_min for a call; the factory-made function's n_min then defaults to NULL, which stands for that value.

...

These dots must be empty.

Details

To export a factory-made function from another package, follow the purrr FAQ on adverbs; see vignette("consistency-tests-in-depth"), section Context and export.

Value

A function such as these:

Factory-made function Reported statistics Test vignette
grim_map_total_n() "x" vignette("grim-in-depth")
grimmer_map_total_n() "x", "sd" vignette("grimmer-in-depth")
debit_map_total_n() "x", "sd" vignette("debit")

The settings of .fn, i.e., its arguments after ..., such as rounding, are arguments of this function as well, with the same defaults. So are its helper arguments, such as items_averaged. Its ... pass any other arguments on to .fn, which is only of use for a .fn without ... of its own.

Conventions

Name the factory-made function after .fn with ⁠_total_n⁠ appended, as in grim_map_total_n() from grim_map(). The output has the classes "scrutiny_map_total_n" and "scrutiny_{tolower(.name_test)}_map_total_n". Prefix any class you add via .name_class with your package's name, as in "mypkg_schlim_map_total_n".

References

Bauer, P. J., & Francis, G. (2021). Expression of Concern: Is It Light or Dark? Recalling Moral Behavior Changes Perception of Brightness. Psychological Science, 32(12), 2042–2043. https://journals.sagepub.com/doi/10.1177/09567976211058727

See Also

function_map_seq()

Examples

# Function definition of `grim_map_total_n()`:
grim_map_total_n <- function_map_total_n(
  .fn = grim_map,
  .reported = "x",
  .name_test = "GRIM"
)

The GRIM test (granularity-related inconsistency of means)

Description

grim() checks whether a reported mean of integer data is consistent with the reported sample size and the number of items that compose the mean. The mean of n * items_averaged integers is a whole number divided by n * items_averaged, so only certain values are possible; the test asks whether any of them would have been reported as x.

The function is vectorized, but grim_map() is more convenient for testing many cases.

Usage

grim(
  x,
  n,
  digits_x,
  items_averaged,
  data_type,
  x_meaning = c("mean", "percentage"),
  rounding = "up_or_down",
  threshold = 5,
  symmetric = FALSE,
  percent = deprecated()
)

Arguments

x

Numeric. The reported mean or percentage.

n

Integer. The reported sample size.

digits_x

Integer. The number of decimal places in x, including trailing zeros. There is no default, since a numeric value does not keep them (1.40 is 1.4).

items_averaged

Integer. The number of items averaged into each respondent's score: 1 for a single item or the percentage of participants with some answer, 4 for the mean of a four-item scale, 10 for the average percent correct on a ten-question test. Not the number of scale points. No default.

data_type

String. The kind of data that x summarizes. No default.

  • "integer": whole numbers, such as answers on a rating scale, counts, or yes/no answers for a percentage.

  • "continuous": anything else, such as body mass index or a visual analogue scale. The test does not apply, so the result is TRUE, or NA for a value set that describes no data, such as one with n = 0.

If data_type is NA, the result is NA unless both types give the same one.

x_meaning

String. What x is: "mean" (the default) or "percentage". A percentage is divided by 100 and tested with two more decimal places, as the proportion it is. See Defaults in grim().

rounding

String. The rounding method assumed to have produced x. Default is "up_or_down", which accepts either direction; see Defaults in grim(). The others are "up_from_or_down_from", "ceiling_or_floor", "up", "down", "even", "up_from", "down_from", "ceiling", "floor", "trunc", "anti_trunc", "ties_up", "ties_down", "ties_away", "ties_zero", and "ties_even". See vignette("rounding-options", package = "roundwork").

threshold

Numeric. For rounding = "up_from", "down_from", or "up_from_or_down_from", the point within a step at which rounding switches direction, in tenths of a step. Default is 5. Ignored otherwise.

symmetric

Logical. If TRUE, negative numbers are rounded like their absolute values (-2.5 like 2.5) under "up", "down", and the "*_from" methods. Default is FALSE. Must not be TRUE with a "ties_*" rounding method, which already fixes the direction; see roundwork::reround().

percent

[Deprecated] Replaced by x_meaning: use x_meaning = "percentage" instead of percent = TRUE.

Details

items_averaged must be specified because a mean over several items is a mean of more values, which changes the test. A default of 1 would let multi-item scales pass unnoticed. data_type must be specified because GRIM only applies to whole numbers. A default of "integer" would let the test flag means of continuous data, which can take any value.

x, n, digits_x, items_averaged, and data_type are vectorized and recycled to a common length by the tidyverse rules: an argument has length 1 or the length of the longest. All other arguments must have length 1.

Numbers beyond the precision of a double are an error rather than a guess. x with its digits_x may have about 15 significant digits: x * 10^(digits_x + 1) must be below 2^52, about 4.5e15, and (x * 10^(digits_x + 1) + 10) * n * items_averaged below 2^53, about 9e15. A threshold with decimal places raises the power of ten.

Value

Logical. TRUE if x, n, and items_averaged are consistent, FALSE if not, and NA if the case cannot be decided: if a value is infinite, if n or items_averaged is not a positive whole number, or if a missing value could decide the case either way. See vignette("technical-notes"), section Missing values, on which cases a missing value leaves open.

Defaults

The defaults are meant to flag a value set only if it is inconsistent under every usual convention:

References

Brown, N. J. L., & Heathers, J. A. J. (2017). The GRIM Test: A Simple Technique Detects Numerous Anomalies in the Reporting of Results in Psychology. Social Psychological and Personality Science, 8(4), 363–369. https://journals.sagepub.com/doi/10.1177/1948550616673876

See Also

grim_explain() to explain a result in plain words, grim_map() for data frames, grim_values() for the means that n integers can produce, and grim_exp_incon() for how informative the test is in a given case.

Examples

# A mean of 5.19 is not consistent with a sample size of 28:
grim(x = 5.19, n = 28, digits_x = 2, items_averaged = 1, data_type = "integer")

# However, it is consistent with a sample size of 32:
grim(x = 5.19, n = 32, digits_x = 2, items_averaged = 1, data_type = "integer")

# For a scale composed of two items:
grim(x = 2.84, n = 16, digits_x = 2, items_averaged = 2, data_type = "integer")

# With percentages instead of means -- here, 71%:
grim(
  x = 71,
  n = 43,
  digits_x = 0,
  items_averaged = 1,
  data_type = "integer",
  x_meaning = "percentage"
)

# Means of continuous data, such as reaction times, can take any value:
grim(
  x = 5.19,
  n = 28,
  digits_x = 2,
  items_averaged = 1,
  data_type = "continuous"
)

Possible GRIM inconsistencies

Description

These functions quantify how informative a GRIM test is for a given sample size and number of decimal places:

See vignette("grim-in-depth"), section GRIM statistics.

Usage

grim_exp_incon(
  x,
  n,
  digits_x,
  items_averaged,
  data_type,
  x_meaning = c("mean", "percentage"),
  rounding = "up_or_down",
  threshold = 5,
  percent = deprecated()
)

grim_total(
  x,
  n,
  digits_x,
  items_averaged,
  data_type,
  x_meaning = c("mean", "percentage"),
  rounding = "up_or_down",
  threshold = 5,
  percent = deprecated()
)

Arguments

x

Numeric. The mean or percentage. Only whether it is missing or infinite matters; the argument mirrors grim().

n

Integer. The sample size.

digits_x

Integer. The number of decimal places in x, including trailing zeros. There is no default, since a numeric value does not keep them (1.40 is 1.4).

items_averaged

Integer. As in grim().

data_type

String. As in grim(). No default. For "continuous" data, which GRIM can't find inconsistent, the result is 0, or NA where grim() is NA.

x_meaning

String. "mean" (the default) or "percentage", which adds 2 to the decimal count, as in grim().

rounding, threshold

As in grim(). The count depends on the method; "even" counts a tie on either bound as consistent, as grim() does. grim_map() passes its own on.

percent

[Deprecated] Replaced by x_meaning: use x_meaning = "percentage" instead of percent = TRUE.

Details

x, n, digits_x, items_averaged, and data_type are recycled to a common length by the tidyverse rules: an argument has length 1 or the length of the longest.

Value

Double. For integer data, grim_exp_incon() and grim_total() return NA if x is missing or infinite, if digits_x is missing, or if n or items_averaged is missing or not a positive whole number. These include every case that grim() cannot decide, and some that it can: a mean may pass whatever n is, but how many fail depends on it. For continuous data, they return 0, or NA where grim() is NA. Under data_type = NA, they return NA unless the count is 0. A count above 2^53, which only digits_x of 14 or more with x_meaning = "percentage" can reach, is the nearest number a double can hold.

References

Brown, N. J. L., & Heathers, J. A. J. (2017). The GRIM Test: A Simple Technique Detects Numerous Anomalies in the Reporting of Results in Psychology. Social Psychological and Personality Science, 8(4), 363–369. https://journals.sagepub.com/doi/10.1177/1948550616673876

See Also

grim() and grim_map().

Examples

# Many value sets are inconsistent here:
grim_exp_incon(
  x = 83.29,
  n = 21,
  digits_x = 2,
  items_averaged = 1,
  data_type = "integer"
)
grim_total(
  x = 83.29,
  n = 21,
  digits_x = 2,
  items_averaged = 1,
  data_type = "integer"
)

# None are in this case...
grim_exp_incon(
  x = 5.14,
  n = 120,
  digits_x = 2,
  items_averaged = 1,
  data_type = "integer"
)
grim_total(
  x = 5.14,
  n = 120,
  digits_x = 2,
  items_averaged = 1,
  data_type = "integer"
)

# ...but most would be if `x` were a percentage:
grim_exp_incon(
  x = 5.14,
  n = 120,
  digits_x = 2,
  items_averaged = 1,
  data_type = "integer",
  x_meaning = "percentage"
)
grim_total(
  x = 5.14,
  n = 120,
  digits_x = 2,
  items_averaged = 1,
  data_type = "integer",
  x_meaning = "percentage"
)

Poisson binomial test for the rate of GRIM consistency

Description

[Experimental]

Given the output of grim_map(), grim_binomial() tests whether the rate of GRIM-consistent value sets is above what random decimal digits would produce. This approach has not been tested extensively; use it with care.

Usage

grim_binomial(
  data,
  alternative = c("greater", "less", "two.sided"),
  alpha = 0.05
)

Arguments

data

Data frame returned by grim_map(). The output of grim_map_seq() or grim_map_total_n() is rejected: its rows are hypothetical value sets, not reported ones.

alternative

String (length 1). One of "greater" (the default), "less", and "two.sided".

alpha

Numeric (length 1). Significance level of the test. The interval around estimate has a confidence level of 1 - alpha. Default is 0.05.

Details

Under the null hypothesis, the decimal digits of each mean are random, so its probability of consistency is 1 - exp_incon, with exp_incon from grim_exp_incon(). Since this differs between value sets, the test uses the Poisson binomial distribution rather than averaging the probabilities. Value sets whose consistency or exp_incon is NA are left out; exp_incon can be NA even where the value set has a definite result (see grim_exp_incon()). Value sets with data_type = "continuous" have an exp_incon of 0, so they count as certain successes: they leave the p-value unchanged but raise estimate and the interval. Filter them out first to compare only the others.

The two-sided p-value is twice the smaller one-sided one, capped at 1.

Correct value sets are always consistent, so a collection of them should be more consistent than random digits even if some are wrong. There is no corresponding reason to expect fewer consistent sets than random, hence the one-sided default.

The confidence interval is the exact Clopper-Pearson interval for the consistency rate, as in stats::binom.test(), with the same alternative as the test. It treats the value sets as sharing one probability, so it is an interval for their average. Compare it to the mean of 1 - exp_incon.

Value

Tibble with the columns estimate (observed consistency rate), statistic (number of consistent cases), p.value, parameter (number of cases), conf.low, conf.high, conf.level (1 - alpha), method, and alternative.

Examples

pigs1 |>
  grim_map(digits_x = 2, items_averaged = 1, data_type = "integer") |>
  grim_binomial()

Power of the GRIM binomial test

Description

[Experimental]

grim_binomial_power() computes the statistical power of grim_binomial() for given parameters, to judge whether the test is informative for a data set before interpreting its p-value. Like the test itself, it has not been tested extensively.

Usage

grim_binomial_power(
  p0,
  p1,
  k = NULL,
  alternative = c("greater", "less", "two.sided"),
  alpha = 0.05
)

Arguments

p0

Numeric. Null probability of GRIM consistency per value set, i.e., 1 - grim_exp_incon(...). Either one value per value set, or a single value shared by all, in which case k is required.

p1

Numeric (length 1). Probability of consistency under the alternative, shared by all value sets.

k

Numeric (length 1). Number of value sets. Required if p0 has length 1; otherwise ignored in favor of length(p0).

alternative

String (length 1). One of "greater" (the default), "less", and "two.sided".

alpha

Numeric (length 1). Significance level. Default is 0.05.

Value

Numeric (length 1). The probability of rejecting the null hypothesis when the alternative is true.

Examples

# 10 means with a null probability of 0.5 each, 90% truly consistent:
grim_binomial_power(p0 = 0.50, p1 = 0.9, k = 10)

# The same with 30 means:
grim_binomial_power(p0 = 0.50, p1 = 0.9, k = 30)

# One null probability per value set:
grim_binomial_power(p0 = c(0.5, 0.7, 0.8, 0.9, 0.6, 0.5, 0.4), p1 = 0.9)

Explain a GRIM test step by step

Description

[Experimental]

grim_explain() tests a single mean like grim() does, but also lays out the calculation in plain words: which true means the reported one could stand for, which sums of the underlying integers follow from them, and whether any of these sums is possible.

The text is built from the numbers that decide the test itself, so it always agrees with grim().

Usage

grim_explain(
  x,
  n,
  digits_x,
  items_averaged,
  data_type,
  x_meaning = c("mean", "percentage"),
  rounding = "up_or_down",
  threshold = 5,
  symmetric = FALSE
)

Arguments

x

Numeric. The reported mean or percentage.

n

Integer. The reported sample size.

digits_x

Integer. The number of decimal places in x, including trailing zeros. There is no default, since a numeric value does not keep them (1.40 is 1.4).

items_averaged

Integer. The number of items averaged into each respondent's score: 1 for a single item or the percentage of participants with some answer, 4 for the mean of a four-item scale, 10 for the average percent correct on a ten-question test. Not the number of scale points. No default.

data_type

String. The kind of data that x summarizes. No default.

  • "integer": whole numbers, such as answers on a rating scale, counts, or yes/no answers for a percentage.

  • "continuous": anything else, such as body mass index or a visual analogue scale. The test does not apply, so the result is TRUE, or NA for a value set that describes no data, such as one with n = 0.

If data_type is NA, the result is NA unless both types give the same one.

x_meaning

String. What x is: "mean" (the default) or "percentage". A percentage is divided by 100 and tested with two more decimal places, as the proportion it is. See Defaults in grim().

rounding

String. The rounding method assumed to have produced x. Default is "up_or_down", which accepts either direction; see Defaults in grim(). The others are "up_from_or_down_from", "ceiling_or_floor", "up", "down", "even", "up_from", "down_from", "ceiling", "floor", "trunc", "anti_trunc", "ties_up", "ties_down", "ties_away", "ties_zero", and "ties_even". See vignette("rounding-options", package = "roundwork").

threshold

Numeric. For rounding = "up_from", "down_from", or "up_from_or_down_from", the point within a step at which rounding switches direction, in tenths of a step. Default is 5. Ignored otherwise.

symmetric

Logical. If TRUE, negative numbers are rounded like their absolute values (-2.5 like 2.5) under "up", "down", and the "*_from" methods. Default is FALSE. Must not be TRUE with a "ties_*" rounding method, which already fixes the direction; see roundwork::reround().

Value

An object of class scrutiny_explain: a list with the result as consistency (TRUE, FALSE, or NA), the explanation's paragraphs, and the test and its inputs. It prints as the explanation.

See Also

grimmer_explain() and debit_explain() for the other tests, and explain_map() for mapper output.

Examples

grim_explain(
  x = 5.19,
  n = 28,
  digits_x = 2,
  items_averaged = 1,
  data_type = "integer"
)

grim_explain(
  x = 5.19,
  n = 32,
  digits_x = 2,
  items_averaged = 1,
  data_type = "integer"
)

grim_explain(
  x = 71,
  n = 43,
  digits_x = 0,
  items_averaged = 1,
  data_type = "integer",
  x_meaning = "percentage"
)

Granularity of non-continuous scales

Description

grim_granularity() returns the smallest difference between two means that a scale with items_averaged items and n observations can produce: 1 / (n * items_averaged).

Usage

grim_granularity(n, items_averaged = 1)

Arguments

n

Numeric. Sample size.

items_averaged

Numeric. Number of items averaged into each score. Default is 1.

Details

The granularity of a scale follows from its design, not from the smallest observed difference between two values. Continuous scales have no granularity.

Value

Numeric. Granularity.

References

Brown, N. J. L., & Heathers, J. A. J. (2017). The GRIM Test: A Simple Technique Detects Numerous Anomalies in the Reporting of Results in Psychology. Social Psychological and Personality Science, 8(4), 363–369. https://journals.sagepub.com/doi/10.1177/1948550616673876

Examples

grim_granularity(n = 16)
grim_granularity(n = 16, items_averaged = 2)

GRIM results on a grid of sample sizes and means

Description

[Experimental]

grim_grid() tests every combination of sample size and fractional part of a mean with GRIM, for one number of decimal places and one rounding method. It is meant for studying GRIM's properties directly. grim_plot_grid() draws the inconsistent cells as its background raster.

Usage

grim_grid(digits_x, data_type, rounding = "up_or_down", threshold = 5)

Arguments

digits_x

Integer. Number of decimal places, from 1 to 3.

data_type

String. "integer" if the values are whole numbers, as in grim(). Under "continuous", every cell is consistent, and under NA, the inconsistent ones are NA.

rounding, threshold

The same as in grim().

Details

The grid covers sample sizes from 1 to 10^digits_x, where GRIM stops ruling out any mean, and fractional parts in steps of 1 / 10^digits_x, without zero. All fractional parts of means are positive, so there is no symmetric argument.

For percentages, use the number of decimal places of the proportion, which is two more: grim_grid(2) for whole percentages, as in grim_map() with x_meaning = "percentage".

Grids for one or two decimal places are precomputed for every rounding method that has no threshold. All others are computed on demand, which takes some 20 to 30 seconds for three decimal places. Four or more would mean a grid of 100 million cells, so digits_x is at most 3.

Value

A tibble with one row per cell of the grid and the columns frac (the fractional part of a mean), n (the sample size), and consistency (the GRIM result).

Examples

grim_grid(digits_x = 1, data_type = "integer")

# Share of means ruled out per sample size:
grim_grid(digits_x = 2, data_type = "integer") |>
  dplyr::summarise(share_inconsistent = mean(!consistency), .by = n)

Number of items from granularity

Description

[Deprecated]

grim_items() is deprecated and will be removed in a future version of scrutiny. Use 1 / (n * gran) instead.

Usage

grim_items(n, gran, tolerance = .Machine$double.eps^0.5)

Arguments

n

Numeric. Sample size.

gran

Numeric. Granularity.

tolerance

Numeric. Differences from a whole number below tolerance are ignored when checking the result. Default is about 1.5e-8.


GRIM-test many cases at once

Description

grim_map() applies grim() to every row of a data frame with means (or percentages) and sample sizes.

Set x_meaning to "percentage" if x contains percentages, and show_rec to TRUE to see the reconstructed values behind each result.

Call these functions on the output of grim_map():

Usage

grim_map(
  data,
  digits_x,
  x = NULL,
  n = NULL,
  ...,
  items_averaged,
  data_type,
  x_meaning = c("mean", "percentage"),
  rounding = "up_or_down",
  threshold = 5,
  symmetric = FALSE,
  show_rec = FALSE
)

Arguments

data

Data frame with columns x and n, and optionally items_averaged, data_type, and digits_x. Any other columns are returned alongside the results.

digits_x

Integer. The number of decimal places in x, including trailing zeros. There is no default, since a numeric value does not keep them (1.40 is 1.4). Either a single number for the whole column or one number per row of data. If left out, it is taken from a digits_x column of data.

x, n

Optionally, the names of the columns in data that contain these values, if they differ from the defaults.

...

These dots must be empty. Every argument after them must be named in full.

items_averaged

Integer. As in grim(): the number of items averaged into each score, not the number of scale points. Give it as an argument or as a column of data; there is no default. It is returned as a column.

data_type

String. As in grim(): "integer" or "continuous". Like items_averaged, give it as an argument or as a column of data; there is no default. It is returned as a column.

x_meaning

String. What the x values are: "mean" (the default) or "percentage". Percentages are divided by 100 and tested with two more decimal places. It applies to the whole call, so test a table that mixes means and percentages in parts, as in the examples of grim_map(). The parts record different settings, so they can't be combined with dplyr::bind_rows(). See Defaults in grim().

rounding, threshold, symmetric

Passed on to grim(), whose section Defaults explains the defaults.

show_rec

Logical. If TRUE, the reconstructed numbers are returned as columns; see Reconstructed numbers in grim_map(). Default is FALSE.

Value

A tibble with these columns:

Reconstructed numbers

With show_rec = TRUE, the output has these further columns, the same ones for every rounding method:

With x_meaning = "percentage", rec_sum is x / 100 times n (times items_averaged), and rec_x_upper and rec_x_lower are percentages like x.

Summaries with audit()

audit() on grim_map() output returns a tibble with one row and these columns:

  1. all_cases: total number of value sets.

  2. testable_cases: number of value sets with a positive exp_incon.

  3. incon_cases: number of GRIM-inconsistent value sets.

  4. emp_incon: proportion of GRIM-inconsistent value sets.

  5. exp_incon: mean probability of GRIM inconsistency.

  6. ratio: ratio of emp_incon to exp_incon, both computed over the value sets that could be decided.

References

Brown, N. J. L., & Heathers, J. A. J. (2017). The GRIM Test: A Simple Technique Detects Numerous Anomalies in the Reporting of Results in Psychology. Social Psychological and Personality Science, 8(4), 363–369. https://journals.sagepub.com/doi/10.1177/1948550616673876

Examples

# Use `grim_map()` on data like these:
pigs1

# The `consistency` column shows whether the
# values to its left are GRIM-consistent:
pigs1 |>
  grim_map(digits_x = 2, items_averaged = 1, data_type = "integer")

# Show the reconstructed numbers:
pigs1 |>
  grim_map(
    digits_x = 2,
    items_averaged = 1,
    data_type = "integer",
    show_rec = TRUE
  )

# Get summaries with `audit()`:
pigs1 |>
  grim_map(digits_x = 2, items_averaged = 1, data_type = "integer") |>
  audit()

# Explain the results in plain words:
pigs1 |>
  grim_map(digits_x = 2, items_averaged = 1, data_type = "integer") |>
  explain_print(1:2)

# A table of means and percentages, tested in parts:
df <- tibble::tibble(
  x = c(5.19, 71),
  n = c(32, 43),
  is_percent = c(FALSE, TRUE)
)

df |>
  dplyr::filter(!is_percent) |>
  grim_map(digits_x = 2, items_averaged = 1, data_type = "integer")

df |>
  dplyr::filter(is_percent) |>
  grim_map(
    digits_x = 0,
    items_averaged = 1,
    data_type = "integer",
    x_meaning = "percentage"
  )

GRIM-test values around the reported ones

Description

grim_map_seq() varies the reported means and sample sizes a few steps up and down and GRIM-tests each resulting value set. This shows whether a small error in reporting or computing could explain an inconsistency.

Call audit() on the output for summary statistics.

Usage

grim_map_seq(
  data,
  digits_x,
  x = NULL,
  n = NULL,
  ...,
  items_averaged,
  data_type,
  x_meaning = c("mean", "percentage"),
  rounding = "up_or_down",
  threshold = 5,
  symmetric = FALSE,
  show_rec = FALSE,
  var = c("x", "n"),
  dispersion = 1:5,
  out_min = "auto",
  out_max = "auto",
  include_reported = FALSE,
  include_consistent = FALSE
)

Arguments

data

A data frame that grim_map() could take.

digits_x

Integer. The number of decimal places in x, including trailing zeros. There is no default, since a numeric value does not keep them (1.40 is 1.4). Either a single number for the whole column or one number per row of data. If left out, it is taken from a digits_x column of data. It also sets the step size for dispersing x.

x, n

Optionally, the names of the columns in data that contain these values, if they differ from the defaults.

...

These dots must be empty. Every argument after them must be named in full.

items_averaged

Integer. As in grim(): the number of items averaged into each score, not the number of scale points. Give it as an argument or as a column of data; there is no default. It is returned as a column.

data_type

String. As in grim(): "integer" or "continuous". Like items_averaged, give it as an argument or as a column of data; there is no default. It is returned as a column.

x_meaning

String. What the x values are: "mean" (the default) or "percentage". Percentages are divided by 100 and tested with two more decimal places. It applies to the whole call, so test a table that mixes means and percentages in parts, as in the examples of grim_map(). The parts record different settings, so they can't be combined with dplyr::bind_rows(). See Defaults in grim().

rounding, threshold, symmetric

Passed on to grim(), whose section Defaults explains the defaults.

show_rec

Logical. If TRUE, the reconstructed numbers are returned as columns; see Reconstructed numbers in grim_map(). Default is FALSE.

var

String. The variables to disperse. Default is c("x", "n").

dispersion

Numeric. Steps up and down from the reported values, in units of the last decimal place. Default is 1:5.

out_min, out_max

Limits on the dispersed values. Both default to "auto", which keeps n at 1 or above and leaves x unbounded. A number applies to every dispersed variable, and NULL removes the limit.

include_reported

Logical. Should the reported values themselves be part of the sequences? Default is FALSE.

include_consistent

Logical. Should consistent value sets be dispersed as well, not just inconsistent ones? Default is FALSE.

Value

A tibble with the columns of grim_map() output plus diff_var, the number of steps between the tested value and the reported one; case, the row number of the reported value set in data; and var, the dispersed variable. Other columns of data, such as study labels, play no role in the test and are dropped; use case to join them back.

Summaries with audit()

audit() on grim_map_seq() output returns a tibble with one row per case and these columns:

A count or distance is NA if it depends on a dispersed value set whose consistency is NA, on a variable whose reported value is missing, which is not dispersed, or on rows removed from the output, as by filter().

Call audit() again on that summary to summarize these columns further. na_count and na_rate there count the cases without a consistent value within the dispersion range, and those that a missing value leaves unknown.

Examples

# `grim_map_seq()` takes the same input as `grim_map()`:
pigs1

out <- grim_map_seq(
  pigs1,
  digits_x = 2,
  items_averaged = 1,
  data_type = "integer",
  include_consistent = TRUE
)
out

# Case-wise summaries:
out |>
  audit()

GRIM-test with hypothetical group sizes

Description

Some studies report group means but only the total sample size, not the group sizes. For two groups, grim_map_total_n() tests hypothetical splits of the total: it starts from an even split, moves one participant at a time from one group to the other, and GRIM-tests both means against both group sizes, in both pairings.

Call audit() on the output for summary statistics.

Usage

grim_map_total_n(
  data,
  digits_x,
  x1 = NULL,
  x2 = NULL,
  n = NULL,
  ...,
  items_averaged,
  data_type,
  x_meaning = c("mean", "percentage"),
  rounding = "up_or_down",
  threshold = 5,
  symmetric = FALSE,
  show_rec = FALSE,
  dispersion = 0:5,
  n_min = 1L,
  n_max = NULL,
  constant = NULL
)

Arguments

data

Data frame with numeric columns x1, x2, and n: the two group means (or percentages) and the total sample size. Which mean is x1 does not matter, since both pairings with the group sizes are tested.

digits_x

Integer. The number of decimal places in x1 and x2, including trailing zeros. There is no default, since a numeric value does not keep them (1.40 is 1.4). Either a single number for both groups, or two numbers, one for x1 and one for x2, which apply to every row of data. If left out, it is taken from a digits_x column of data, with one number per row for both groups. If the decimal places differ both by group and by row, call the function on each set of rows separately and audit() each output.

x1, x2, n

Optionally, the names of the columns in data that contain these values, if they differ from the defaults.

...

These dots must be empty. Every argument after them must be named in full.

items_averaged

Integer. As in grim(): the number of items averaged into each score, not the number of scale points. Give it as an argument or as a column of data; there is no default. It is returned as a column.

data_type

String. As in grim(): "integer" or "continuous". Like items_averaged, give it as an argument or as a column of data; there is no default. It is returned as a column.

x_meaning

String. What the x values are: "mean" (the default) or "percentage". Percentages are divided by 100 and tested with two more decimal places. It applies to the whole call, so test a table that mixes means and percentages in parts, as in the examples of grim_map(). The parts record different settings, so they can't be combined with dplyr::bind_rows(). See Defaults in grim().

rounding, threshold, symmetric

Passed on to grim(), whose section Defaults explains the defaults.

show_rec

Logical. If TRUE, the reconstructed numbers are returned as columns; see Reconstructed numbers in grim_map(). Default is FALSE.

dispersion

Numeric. Steps up and down from half the total n. Default is 0:5, i.e., the even split and five steps away from it.

n_min

Numeric. Minimal group size. Default is 1.

n_max

Numeric. Maximal group size. Default is NULL, i.e., no maximum.

constant

Optionally, values that belong to the two groups, such as their labels: a length-2 vector for the groups of x1 and x2, or a list of such vectors. Each becomes a column, as in disperse_total(), and its values stay with their groups in both directions. Default is NULL.

Value

A tibble with these columns:

Summaries with audit()

audit() on grim_map_total_n() output returns a tibble with one row per case and these columns:

A count is NA if it depends on a scenario that could not be decided, or on rows removed from the output, as by filter().

Call audit() again on that summary to summarize these columns further.

References

Bauer, P. J., & Francis, G. (2021). Expression of Concern: Is It Light or Dark? Recalling Moral Behavior Changes Perception of Brightness. Psychological Science, 32(12), 2042–2043. https://journals.sagepub.com/doi/10.1177/09567976211058727

Brown, N. J. L., & Heathers, J. A. J. (2017). The GRIM Test: A Simple Technique Detects Numerous Anomalies in the Reporting of Results in Psychology. Social Psychological and Personality Science, 8(4), 363–369. https://journals.sagepub.com/doi/10.1177/1948550616673876

See Also

function_map_total_n(), which created this function from grim_map().

Examples

df <- tibble::tribble(
  ~x1,  ~x2,  ~n,
  3.43, 5.28, 90,
  2.97, 4.42, 103
)

grim_map_total_n(df, digits_x = 2, items_averaged = 1, data_type = "integer")

# `hits_total` counts the splits in which both means are consistent:
df |>
  grim_map_total_n(digits_x = 2, items_averaged = 1, data_type = "integer") |>
  audit()

# More steps away from the even split tend to find more hits:
df |>
  grim_map_total_n(
    digits_x = 2,
    items_averaged = 1,
    data_type = "integer",
    dispersion = 0:10
  ) |>
  audit()

Deprecated name of grim_plot_grid()

Description

[Deprecated]

grim_plot() was renamed to grim_plot_grid() in scrutiny 1.0.0. It keeps the signature it had in scrutiny 0.6.1, so that calls by position still work.

Usage

grim_plot(
  data = NULL,
  show_data = TRUE,
  show_raster = TRUE,
  show_gradient = TRUE,
  n = NULL,
  digits = NULL,
  rounding = "up_or_down",
  color_cons = deprecated(),
  color_incons = deprecated(),
  tile_alpha = deprecated(),
  tile_size = 1.5,
  raster_alpha = deprecated(),
  raster_color = NULL,
  ...
)

Arguments

data, show_data, show_raster, show_gradient, n, rounding, tile_size

Passed on to grim_plot_grid().

digits, raster_color

The old names of grim_plot_grid()'s digits_x and color_raster.

color_cons, color_incons, tile_alpha, raster_alpha

No longer have any effect. To recolor the tiles, add a color scale, as in the examples of grim_plot_grid().

...

Other arguments of grim_plot_grid(), such as split_by_digits.


Visualize GRIM test results

Description

grim_plot_grid() plots the output of grim_map() or grimmer_map(). Each value set is placed by n * items_averaged (the sample size for single items) and the fractional part of its mean, in blue if consistent and red if not, on a background raster that shows which combinations are GRIM-consistent at all.

Usage

grim_plot_grid(
  data = NULL,
  show_data = TRUE,
  show_raster = TRUE,
  show_gradient = TRUE,
  split_by_digits = FALSE,
  n = NULL,
  digits_x = NULL,
  rounding = "up_or_down",
  threshold = 5,
  tile_size = 1.5,
  color_raster = "grey75"
)

Arguments

data

Data frame returned by grim_map() or grimmer_map(), or by their ⁠*_seq()⁠ and ⁠*_total_n()⁠ variants.

show_data

Logical. If FALSE, only the raster is drawn. Default is TRUE.

show_raster

Logical. Should the raster be drawn? Default is TRUE.

show_gradient

Logical. With three or more decimal places, should a gradient be drawn? Default is TRUE.

split_by_digits

Logical. If TRUE, return a named list with one plot per number of decimal places instead of erroring when they differ. Cannot be combined with digits_x. Default is FALSE.

n

Integer. Maximal value on the x-axis. Default is NULL, in which case the axis spans the raster and the data.

digits_x

Integer. Number of decimal places for the raster. Default is NULL, in which case it is read from the digits_x column that every mapper adds, or, if that was removed, inferred from x, which loses trailing zeros. With show_data = FALSE, NULL means 2. For percentages, give the number of decimal places as reported; 2 are added.

rounding, threshold

The same as in grim(), for the raster. Only used if show_data is FALSE: otherwise, the raster follows the settings of the mapper call, and giving others is an error. Defaults are "up_or_down" and 5.

tile_size

Numeric. Size of the tiles for value sets. Default is 1.5.

color_raster

String. Color of the consistent cells of the raster, and of the consistent end of the gradient that replaces it with three or more decimal places. Default is "grey75".

Value

A ggplot object. With split_by_digits = TRUE, a named list of ggplot objects, one per distinct non-zero number of decimal places (e.g., list(digits_1 = ..., digits_2 = ...)), returned invisibly after each plot is printed.

Background raster

For one or two decimal places, the raster shows every combination of sample size and fractional part that is GRIM-consistent in grey and every inconsistent one in white. It depends on the number of decimal places, which is read from the digits_x column of data, and on the rounding method, which is read from the grim_map() call. The white tiles are the cells of grim_grid() whose consistency is FALSE. With three or more decimal places, they would be too small, so a gradient shows the probability of inconsistency instead.

A raster only applies to one number of decimal places, so grim_plot_grid() errors if digits_x varies across rows. Use split_by_digits = TRUE to get one plot per number of decimal places instead, or digits_x to choose one.

With x_meaning = "percentage" in the grim_map() call, percentages are plotted as the proportions they were tested as, with two more decimal places.

Set show_data = FALSE to plot the raster alone, controlled by digits_x, rounding, threshold, and n.

Under the default rounding = "up_or_down", fewer values are inconsistent at sample sizes 40 and 80 than at their neighbors with two decimal places (4 and 8 with one), and none at 80 (or 8), because many achievable means there are ties, which the default accepts in both directions.

Negative means

The y-axis is the fractional part of the absolute value of x, so a mean of -2.51 is drawn at 0.51. GRIM's granularity is symmetric around zero, so this is the value set's place on the grid. A one-directional rounding method treats the absolute value of a negative number the other way: "ceiling" like "floor", and "up" like "down" (unless symmetric = TRUE). If all means are negative, the raster is the one for this mirrored method, so that it agrees with the tiles. With positive and negative means under such a method, no single raster does; it is then the one for positive means, with a warning. The tile's color comes from the consistency column and is unaffected.

References

Brown, N. J. L., & Heathers, J. A. J. (2017). The GRIM Test: A Simple Technique Detects Numerous Anomalies in the Reporting of Results in Psychology. Social Psychological and Personality Science, 8(4), 363–369. https://journals.sagepub.com/doi/10.1177/1948550616673876

Examples

# `grim_plot_grid()` reads the number of decimal places
# from the `digits_x` column:
pigs1 |>
  grim_map(digits_x = 2, items_averaged = 1, data_type = "integer") |>
  grim_plot_grid()

# The raster follows the rounding method of the `grim_map()` call:
pigs1 |>
  grim_map(
    digits_x = 2,
    items_averaged = 1,
    data_type = "integer",
    rounding = "ceiling"
  ) |>
  grim_plot_grid()

# Percentages are plotted as proportions:
pigs2 |>
  grim_map(
    digits_x = 1,
    items_averaged = 1,
    data_type = "integer",
    x_meaning = "percentage"
  ) |>
  grim_plot_grid()

# Other colors for the value sets:
pigs1 |>
  grim_map(digits_x = 2, items_averaged = 1, data_type = "integer") |>
  grim_plot_grid() +
  ggplot2::scale_colour_manual(
    values = c("TRUE" = "#2f7d74", "FALSE" = "#c1522e"),
    aesthetics = c("colour", "fill"),
    guide = "none"
  )

Visualize the means that GRIM admits

Description

[Experimental]

grim_plot_line() draws the GRIM test for a single value set as a row of squares. Of all means (or percentages) that could have been reported with digits_x decimal places, each square is one that a sample of size n can have; the others are left out. The reported value is marked above the row and labeled as consistent or inconsistent.

grim_plot_grid() shows many results at once; this function explains one. grim_values() lists the unrounded means behind a reported one.

Usage

grim_plot_line(
  x,
  n,
  digits_x,
  items_averaged,
  data_type,
  x_meaning = c("mean", "percentage"),
  rounding = "up_or_down",
  threshold = 5,
  symmetric = FALSE,
  zoom = c("close", "medium", "wide"),
  color_con = "#0047ff",
  color_incon = "#ff2a00",
  color_attainable = "grey75"
)

Arguments

x, n, digits_x, items_averaged, x_meaning, rounding, threshold, symmetric

The same as in grim(), but x, n, digits_x, and items_averaged must have length 1.

data_type

String. "integer" if the values are whole numbers, as in grim(). There is no default, so that every call states this assumption. "continuous" and NA are errors: if the data may be continuous, there are no values to draw.

zoom

String. How much of the row to show: "close" (the default), "medium", or "wide"; see Details.

color_con, color_incon

Strings. Color of the marker, the strip behind the squares, and the square at the reported value if it is consistent. color_con applies if the value is consistent, color_incon if not. Defaults are "#0047ff" and "#ff2a00".

color_attainable

String. Color of the squares for attainable values that were not reported, like the consistent cells of grim_plot_grid()'s raster. Default is "grey75".

Details

With zoom = "close", the row shows 10 values on either side of x, so that the marker and the strip behind it stand out. If x is inconsistent, it widens until the nearest attainable value is in view, but no farther than the row with zoom = "wide" is wide. It does not widen further for the nearest one on the other side: attainable percentages in particular can be dozens of values apart, so the squares and the strip would shrink to specks.

With zoom = "medium", the row widens until the nearest attainable value other than x is in view on either side, whether x is consistent or not, which shows how far apart the attainable values are. Again, it widens no farther than the row with zoom = "wide". Both settings test at most 20,000 values, and warn if they stop short of an attainable value for that reason.

Each attainable value is labeled below its square unless the squares are too close together for that, as they mostly are with zoom = "wide". Then the axis has the usual breaks, and if x is inconsistent, the nearest attainable values are labeled above the row.

With zoom = "wide", the row runs from the whole number at or below x to the next one up. With items_averaged = 1, about n of the values in this unit are attainable, or all if n exceeds 10 ^ digits_x. With x_meaning = "percentage", the unit is that of the proportion GRIM tests, so the row runs from 0% to 100%, and about n + 1 values are attainable, or all if n exceeds 10 ^ (digits_x + 2). Squares too narrow to be seen as such are drawn as ticks of the same height.

The strip behind the squares is the range of unrounded means that would have been reported as x, from roundwork::unround(). The value is consistent exactly if a square stands within the strip. Under methods such as "trunc" or "ceiling", one edge of the strip is not part of it, so a square on that edge does not count.

Value

A ggplot object.

See Also

grimmer_plot_line() for the same figure over standard deviations.

Examples

# A mean of 5.19 is not consistent with a sample size of 28:
# no square stands where the marker points.
grim_plot_line(x = 5.19, n = 28, digits_x = 2, items_averaged = 1, data_type = "integer")

# With `n = 26`, it is:
grim_plot_line(x = 5.19, n = 26, digits_x = 2, items_averaged = 1, data_type = "integer")

# The same for a percentage:
grim_plot_line(
  x = 67.4,
  n = 40,
  digits_x = 1,
  items_averaged = 1,
  data_type = "integer",
  x_meaning = "percentage"
)

Compare GRIM inconsistency rates to random means

Description

[Experimental]

grim_plot_rates() plots one point per study: its observed rate of GRIM-inconsistent value sets against the rate that means with random decimal digits would show at the same sample sizes and numbers of decimal places. This helps spot both problematic studies and problems in GRIM usage. See Details.

Correctly reported means are always consistent, so a study off the dotted line at zero has a problem, either in the study or in the GRIM analysis. The dashed diagonal marks studies where GRIM inconsistencies are as frequent as expected if the means' digits were random.

Usage

grim_plot_rates(data, study)

Arguments

data

Data frame returned by grim_map().

study

<tidy-select> Column of data that identifies the study of each value set.

Details

The expected rate of a study is the mean of the exp_incon column, from grim_exp_incon(). Value sets whose exp_incon is zero or missing are left out because GRIM cannot test them; if none remain, that is an error. Value sets without a study are left out with a warning.

Point area is the number of GRIM-testable value sets. Color shows the ratio of the observed rate to the expected one: 0 means no inconsistencies, 1 as many as random means. Studies under the diagonal are above 1, in darker colors.

Nothing should push a study's rate above chance, so a large point clearly below the diagonal points to an error in the GRIM analysis. A small point can land there by chance.

Value

A ggplot object.

See Also

grim_binomial() for a test of the same comparison.

Examples


# Assume each unique code indicates one study
codes <- c(
  "B", "B", "C", "C", "A", "C",
  "A", "D", "A", "A", "A", "A"
)

pigs1 |>
  grim_map(digits_x = 2, items_averaged = 1, data_type = "integer") |>
  dplyr::mutate(study = codes) |>
  grim_plot_rates(study)


Deprecated name of grim_exp_incon()

Description

[Deprecated]

grim_probability() was renamed to grim_exp_incon() in scrutiny 1.0.0, which also requires digits_x and data_type, takes items_averaged instead of items and x_meaning instead of percent, and has a rounding argument.

Usage

grim_probability(...)

Arguments

...

Passed on to grim_exp_incon().


Achievable means behind a reported one

Description

[Experimental]

These functions return the means (or percentages) that a sample of n * items_averaged integers can have:

Both are vectorized like grim(); case says which value set a row belongs to. grimmer_values() and grimmer_closest() are the counterparts for GRIMMER.

Usage

grim_values(
  x,
  n,
  digits_x,
  items_averaged,
  data_type,
  x_meaning = c("mean", "percentage"),
  rounding = "up_or_down",
  threshold = 5,
  symmetric = FALSE
)

grim_closest(
  x,
  n,
  digits_x,
  items_averaged,
  data_type,
  x_meaning = c("mean", "percentage"),
  rounding = "up_or_down",
  threshold = 5,
  symmetric = FALSE
)

Arguments

x

Numeric. The reported mean or percentage.

n

Integer. The reported sample size.

digits_x

Integer. The number of decimal places in x, including trailing zeros. There is no default, since a numeric value does not keep them (1.40 is 1.4).

items_averaged

Integer. As in grim().

data_type

String. "integer" if the values are whole numbers, as in grim(). There is no default, so that every call states this assumption. "continuous" is an error: such data can have any value that would be reported as the one given, so there are none to list. Under NA, a value set is undecidable unless no data can have it.

x_meaning

String. "mean" (the default) or "percentage", as in grim().

rounding, threshold, symmetric

The same as in grim().

Details

The achievable means are the whole numbers in the range that grim_map() shows as sum_lower and sum_upper under show_rec, each divided by n * items_averaged. Once n * items_averaged exceeds 10^digits_x, several of them can be reported as the same x, and their number grows with n * items_averaged. GRIM is then no longer informative. grim_closest() stays cheap in that case. grim_values() does not, and more than a million values for one value set is an error.

With x_meaning = "percentage", the values are percentages, like x.

Under rounding = "even", or its synonym "ties_even", both bounds of that range are inclusive (see roundwork::unround()), so the values include ties that base::round() may round away from x: grim_values(1.3, 20, 1, 1, "integer", rounding = "even") returns 1.25 and 1.35 along with 1.3.

With items_averaged > 1, the integers are the respondents' totals across the items, so sum is the sum of the totals.

Value

References

Brown, N. J. L., & Heathers, J. A. J. (2017). The GRIM Test: A Simple Technique Detects Numerous Anomalies in the Reporting of Results in Psychology. Social Psychological and Personality Science, 8(4), 363–369. https://journals.sagepub.com/doi/10.1177/1948550616673876

See Also

grim(), grim_map(), grimmer_values(), and grim_exp_incon().

Examples

# A mean of 5.19 is consistent with a sample size of 32,
# and there is exactly one way to get there:
grim_values(x = 5.19, n = 32, digits_x = 2, items_averaged = 1, data_type = "integer")

# With a larger sample, several means round to 5.19:
grim_values(x = 5.19, n = 300, digits_x = 2, items_averaged = 1, data_type = "integer")

# It is not consistent with a sample size of 28...
grim_values(x = 5.19, n = 28, digits_x = 2, items_averaged = 1, data_type = "integer")

# ...but 5.19 is close to a mean that is achievable:
grim_closest(x = 5.19, n = 28, digits_x = 2, items_averaged = 1, data_type = "integer")

# Both functions are vectorized. In `grim_values()`, `case` is the index of
# the value set, and an inconsistent one has no rows:
grim_values(
  x = c(5.19, 4.2),
  n = c(28, 30),
  digits_x = 2,
  items_averaged = 1,
  data_type = "integer"
)
grim_closest(
  x = c(5.19, 4.2),
  n = c(28, 30),
  digits_x = 2,
  items_averaged = 1,
  data_type = "integer"
)

The GRIMMER test (granularity-related inconsistency of means mapped to error repeats)

Description

grimmer() checks whether a reported mean and standard deviation of integer data are consistent with the reported sample size and the number of items averaged. It extends grim(): the mean must pass GRIM, and the SD must then be reachable from one of the integer sums that GRIM admits.

The function is vectorized, but grimmer_map() is more convenient for testing many cases.

Usage

grimmer(
  x,
  sd,
  n,
  digits_x,
  digits_sd,
  items_averaged,
  data_type,
  sd_meaning = c("sd_sample", "sd_pop", "sd_sample_or_pop", "se"),
  rounding = "up_or_down",
  threshold = 5,
  symmetric = FALSE
)

Arguments

x

Numeric. The reported mean.

sd

Numeric. The reported standard deviation.

n

Integer. The reported sample size.

digits_x

Integer. The number of decimal places in x, including trailing zeros. There is no default, since a numeric value does not keep them (1.40 is 1.4).

digits_sd

Integer. The number of decimal places in sd, including trailing zeros. There is no default, since a numeric value does not keep them (1.40 is 1.4).

items_averaged

Integer. As in grim(), for x and sd.

data_type

String. As in grim(): "integer" or "continuous". No default. Continuous data can have any mean and any SD of 0 or more, but a single value only an SD of 0.

sd_meaning

String. What sd really is. "sd_sample" (the default) is the sample SD, which divides by n - 1; "sd_pop" is the population SD, which divides by n; "sd_sample_or_pop" accepts either; "se" is the standard error of the mean, the sample SD divided by sqrt(n), reported as if it were an SD. See Defaults and SD meaning in grimmer().

rounding

String. The rounding method assumed to have produced x. Default is "up_or_down", which accepts either direction; see Defaults in grim(). The others are "up_from_or_down_from", "ceiling_or_floor", "up", "down", "even", "up_from", "down_from", "ceiling", "floor", "trunc", "anti_trunc", "ties_up", "ties_down", "ties_away", "ties_zero", and "ties_even". See vignette("rounding-options", package = "roundwork").

threshold

Numeric. For rounding = "up_from", "down_from", or "up_from_or_down_from", the point within a step at which rounding switches direction, in tenths of a step. Default is 5. Ignored otherwise.

symmetric

Logical. If TRUE, negative numbers are rounded like their absolute values (-2.5 like 2.5) under "up", "down", and the "*_from" methods. Default is FALSE. Must not be TRUE with a "ties_*" rounding method, which already fixes the direction; see roundwork::reround().

Details

x, sd, n, digits_x, digits_sd, items_averaged, and data_type are vectorized and recycled to a common length by the tidyverse rules. All other arguments must have length 1.

The implementation follows Allard's (2018) Analytic-GRIMMER algorithm, as does rsprite2::GRIMMER_test(), with a stricter first test: the sum of squares must be one that n whole numbers with the reconstructed sum can have. For n >= 5, this means at least the smallest such sum of squares; for smaller n, some larger ones are impossible, too. With this test, grimmer() is TRUE if and only if a sample of whole numbers has a mean and SD that would be reported as x and sd. The one exception is rounding = "even", or its synonym "ties_even", which takes a value on a rounding bound to round either way, as in roundwork::unround(), so it may also accept a value set that a sample only rounds to if ties are broken the other way. GRIMMER was first proposed by Anaya (2016).

For data on a bounded scale, such as a Likert scale, see the strait package. Its brimmest() decides exactly whether a mean and SD are possible within the bounds.

x and sd are limited to about 15 significant digits, and x with n and items_averaged as in grim(). Within these limits, grimmer() decides value sets of any size exactly, but refuses two kinds that no study reports: with n from 2 to 4, SDs above about 3e5 to 1e6 (divided by items_averaged), depending on n and sd_meaning; and value sets with more than a million candidate sums, which takes an n of more than a million.

Value

Logical. TRUE if x, sd, n, and items_averaged are consistent, FALSE if not, and NA if the case cannot be decided: if a value is infinite, if n or items_averaged is not a positive whole number, or if a missing value could decide the case either way. See vignette("technical-notes"), section Missing values, on which cases a missing value leaves open. A sample SD divides by n - 1, so under sd_meaning = "sd_sample" (the default) and "se", n must also be at least 2.

Defaults

sd_meaning = "sd_sample" because the sample SD is what nearly all software reports by default: R's sd(), SPSS, Stata, SAS, Excel's STDEV() and STDEV.S(), and pandas' .std(). The main exception is NumPy, whose np.std() returns the population SD unless ddof = 1 is given; so does Python's statistics.pstdev(). If the data may have been analyzed that way, or an inconsistent value set has a small n, test it under "sd_pop" as well. See SD meaning.

The rounding defaults are explained in grim().

SD meaning

The sample and population SDs differ by a factor of sqrt(n / (n - 1)), which matters most at small n, where GRIMMER is most powerful. A population SD tested as a sample SD will often seem inconsistent, so sd_meaning lets you check whether a value set is consistent only under the other convention.

"sd_sample_or_pop" is TRUE if the value set is consistent as a sample SD or as a population SD, and FALSE if it is neither. A single value has no sample SD, so at n = 1, "sd_sample_or_pop" is the same as "sd_pop". The reason column of grimmer_map() names the convention a value set passed under, trying the sample SD first.

"sd_sample_or_pop" accepts more SDs than either convention alone, so it is not the default. It also judges each value set on its own, although a single study usually computed all its SDs the same way. To tell which convention fits a set of values, test them under "sd_sample" and "sd_pop" separately.

Some papers report a standard error as if it were an SD. "se" tests whether a value set is consistent under that premise. The SE was rounded, not the SD, so the SD may lie anywhere in the SE's rounding range times sqrt(n). This range is wide: from about n = 30 on, nearly every value set whose mean passes GRIM is consistent under "se". Such a result says little, and it is no evidence that an SE was reported.

"se" is the SE of this group's mean, computed from its own sample SD and n. Model-based SEs, such as those of estimated marginal means, or SEs based on a pooled variance, are not. A value set that is inconsistent under "se" may still have been reported with one of them.

References

Allard, A. (2018). Analytic-GRIMMER: a new way of testing the possibility of standard deviations. https://aurelienallard.netlify.app/post/anaytic-grimmer-possibility-standard-deviations/

Anaya, J. (2016). The GRIMMER test: A method for testing the validity of reported measures of variability. PeerJ Preprints. https://peerj.com/preprints/2400v1/

See Also

grimmer_explain() to explain a result in plain words, grimmer_map() for data frames, and grimmer_values() for the means and SDs behind a consistent value set.

Examples

# A mean of 5.23 is not consistent with an SD of 2.55
# and a sample size of 35:
grimmer(
  x = 5.23,
  sd = 2.55,
  n = 35,
  digits_x = 2,
  digits_sd = 2,
  items_averaged = 1,
  data_type = "integer"
)

# However, mean and SD are consistent with a
# sample size of 31:
grimmer(
  x = 5.23,
  sd = 2.55,
  n = 31,
  digits_x = 2,
  digits_sd = 2,
  items_averaged = 1,
  data_type = "integer"
)

# For a scale composed of two items:
grimmer(
  x = 2.74,
  sd = 0.96,
  n = 63,
  digits_x = 2,
  digits_sd = 2,
  items_averaged = 2,
  data_type = "integer"
)

Explain a GRIMMER test step by step

Description

[Experimental]

grimmer_explain() tests a single mean and SD like grimmer() does, but also lays out the calculation in plain words. It starts with the GRIM test of the mean, as in grim_explain(), and goes on to the sum of squares that each possible sum implies with the SD.

The text is built from the numbers that decide the test itself, so it always agrees with grimmer().

Usage

grimmer_explain(
  x,
  sd,
  n,
  digits_x,
  digits_sd,
  items_averaged,
  data_type,
  sd_meaning = c("sd_sample", "sd_pop", "sd_sample_or_pop", "se"),
  rounding = "up_or_down",
  threshold = 5,
  symmetric = FALSE
)

Arguments

x

Numeric. The reported mean.

sd

Numeric. The reported standard deviation.

n

Integer. The reported sample size.

digits_x

Integer. The number of decimal places in x, including trailing zeros. There is no default, since a numeric value does not keep them (1.40 is 1.4).

digits_sd

Integer. The number of decimal places in sd, including trailing zeros. There is no default, since a numeric value does not keep them (1.40 is 1.4).

items_averaged

Integer. As in grim(), for x and sd.

data_type

String. As in grim(): "integer" or "continuous". No default. Continuous data can have any mean and any SD of 0 or more, but a single value only an SD of 0.

sd_meaning

String. What sd really is. "sd_sample" (the default) is the sample SD, which divides by n - 1; "sd_pop" is the population SD, which divides by n; "sd_sample_or_pop" accepts either; "se" is the standard error of the mean, the sample SD divided by sqrt(n), reported as if it were an SD. See Defaults and SD meaning in grimmer().

rounding

String. The rounding method assumed to have produced x. Default is "up_or_down", which accepts either direction; see Defaults in grim(). The others are "up_from_or_down_from", "ceiling_or_floor", "up", "down", "even", "up_from", "down_from", "ceiling", "floor", "trunc", "anti_trunc", "ties_up", "ties_down", "ties_away", "ties_zero", and "ties_even". See vignette("rounding-options", package = "roundwork").

threshold

Numeric. For rounding = "up_from", "down_from", or "up_from_or_down_from", the point within a step at which rounding switches direction, in tenths of a step. Default is 5. Ignored otherwise.

symmetric

Logical. If TRUE, negative numbers are rounded like their absolute values (-2.5 like 2.5) under "up", "down", and the "*_from" methods. Default is FALSE. Must not be TRUE with a "ties_*" rounding method, which already fixes the direction; see roundwork::reround().

Value

An object of class scrutiny_explain: a list with the result as consistency (TRUE, FALSE, or NA), the explanation's paragraphs, and the test and its inputs. It prints as the explanation.

Examples

grimmer_explain(
  x = 5.23,
  sd = 2.55,
  n = 35,
  digits_x = 2,
  digits_sd = 2,
  items_averaged = 1,
  data_type = "integer"
)

grimmer_explain(
  x = 5.23,
  sd = 2.55,
  n = 31,
  digits_x = 2,
  digits_sd = 2,
  items_averaged = 1,
  data_type = "integer"
)

GRIMMER-test many cases at once

Description

grimmer_map() applies grimmer() to every row of a data frame with means, standard deviations, and sample sizes.

Call audit() on the output for summary statistics. grim_plot_grid() accepts it as well.

Usage

grimmer_map(
  data,
  digits_x,
  digits_sd,
  x = NULL,
  sd = NULL,
  n = NULL,
  ...,
  items_averaged,
  data_type,
  sd_meaning = c("sd_sample", "sd_pop", "sd_sample_or_pop", "se"),
  rounding = "up_or_down",
  threshold = 5,
  symmetric = FALSE
)

Arguments

data

Data frame with columns x, sd, and n, and optionally items_averaged, data_type, digits_x, and digits_sd. Any other columns are returned alongside the results.

digits_x, digits_sd

Integer. The number of decimal places in x and sd, including trailing zeros. There are no defaults, since a numeric value does not keep them (1.40 is 1.4). Either a single number for the whole column or one number per row of data. If left out, they are taken from digits_x and digits_sd columns of data.

x, sd, n

Optionally, the names of the columns in data that contain these values, if they differ from the defaults.

...

These dots must be empty. Every argument after them must be named in full.

items_averaged, data_type

As in grim_map(), for x and sd.

rounding, threshold, symmetric, sd_meaning

Passed on to grimmer(), whose section Defaults explains the defaults.

Value

A tibble with these columns:

The reason column refers to GRIM and the GRIMMER tests of Allard (2018):

  1. The reconstructed sum of squared observations must be a whole number, and one that n whole numbers with the reconstructed sum can reach: at least the sum of squares of these numbers as close together as possible. For n < 5, some larger sums of squares are impossible, too.

  2. The reconstructed SD must match the reported one. Test 1 implies this, so it is never a reason.

  3. The parity of the reconstructed sum of squared observations must match that of the reconstructed sum of the values.

A negative sd gives "Negative SD".

Under sd_meaning = "sd_sample_or_pop", a consistent value set gives "Passed all (sample SD)" or, if it passes as a population SD only, "Passed all (population SD)". If the two conventions fail for different reasons, the reason names both, and audit() counts the sample SD's, as under the default sd_meaning.

The tibble has the class scrutiny_grimmer_map, which is recognized by audit() and grim_plot_grid().

Summaries with audit()

audit() on grimmer_map() output returns a tibble with one row and these columns:

  1. all_cases: total number of value sets.

  2. incon_cases: number of GRIMMER-inconsistent value sets.

  3. emp_incon: proportion of GRIMMER-inconsistent value sets.

  4. fail_grim: number of value sets that fail GRIM.

  5. fail_test1, fail_test3: number of value sets that fail GRIMMER's first or third test. Test 2 is implied by test 1, so it never fails.

References

Allard, A. (2018). Analytic-GRIMMER: a new way of testing the possibility of standard deviations. https://aurelienallard.netlify.app/post/anaytic-grimmer-possibility-standard-deviations/

Anaya, J. (2016). The GRIMMER test: A method for testing the validity of reported measures of variability. PeerJ Preprints. https://peerj.com/preprints/2400v1/

See Also

explain_map() and explain_print() to explain each row's result in plain words.

Examples

# Use `grimmer_map()` on data like these:
pigs5

# The `consistency` column shows whether the values to its left are
# GRIMMER-consistent. If they aren't, the `reason` column gives the reason:
pigs5 |>
  grimmer_map(
    digits_x = 2,
    digits_sd = 2,
    items_averaged = 1,
    data_type = "integer"
  )

# Get summaries with `audit()`:
pigs5 |>
  grimmer_map(
    digits_x = 2,
    digits_sd = 2,
    items_averaged = 1,
    data_type = "integer"
  ) |>
  audit()

# Explain the results in plain words:
pigs5 |>
  grimmer_map(
    digits_x = 2,
    digits_sd = 2,
    items_averaged = 1,
    data_type = "integer"
  ) |>
  explain_print(1:2)

GRIMMER-test values around the reported ones

Description

grimmer_map_seq() varies the reported means, standard deviations, and sample sizes a few steps up and down and GRIMMER-tests each resulting value set. This shows whether a small error in reporting or computing could explain an inconsistency.

Call audit() on the output for summary statistics.

Usage

grimmer_map_seq(
  data,
  digits_x,
  digits_sd,
  x = NULL,
  sd = NULL,
  n = NULL,
  ...,
  items_averaged,
  data_type,
  sd_meaning = c("sd_sample", "sd_pop", "sd_sample_or_pop", "se"),
  rounding = "up_or_down",
  threshold = 5,
  symmetric = FALSE,
  var = c("x", "sd", "n"),
  dispersion = 1:5,
  out_min = "auto",
  out_max = "auto",
  include_reported = FALSE,
  include_consistent = FALSE
)

Arguments

data

A data frame that grimmer_map() could take.

digits_x, digits_sd

Integer. The number of decimal places in x and sd, including trailing zeros. There are no defaults, since a numeric value does not keep them (1.40 is 1.4). Either a single number for the whole column or one number per row of data. If left out, they are taken from digits_x and digits_sd columns of data. They also set the step sizes for dispersing x and sd.

x, sd, n

Optionally, the names of the columns in data that contain these values, if they differ from the defaults.

...

These dots must be empty. Every argument after them must be named in full.

items_averaged

Integer. As in grimmer_map(): the number of items averaged into each score, given as an argument or as a column of data. There is no default.

data_type

String. As in grim(): "integer" or "continuous". Like items_averaged, give it as an argument or as a column of data; there is no default. It is returned as a column.

rounding, threshold, symmetric, sd_meaning

Passed on to grimmer(), whose section Defaults explains the defaults.

var

String. The variables to disperse. Default is c("x", "sd", "n").

dispersion

Numeric. Steps up and down from the reported values, in units of the last decimal place. Default is 1:5.

out_min, out_max

Limits on the dispersed values. Both default to "auto", which keeps n at 2 or above (1 under sd_meaning = "sd_pop" or "sd_sample_or_pop") and sd at 0 or above, and leaves x unbounded. A number applies to every dispersed variable, and NULL removes the limit.

include_reported

Logical. Should the reported values themselves be part of the sequences? Default is FALSE.

include_consistent

Logical. Should consistent value sets be dispersed as well, not just inconsistent ones? Default is FALSE.

Value

A tibble with the columns of grimmer_map() output plus diff_var, case, and var, as in grim_map_seq(). As there, the other columns of data are dropped.

Summaries with audit()

audit() on grimmer_map_seq() output returns a tibble with one row per case and these columns:

A count or distance is NA if it depends on a dispersed value set whose consistency is NA, on a variable whose reported value is missing, which is not dispersed, or on rows removed from the output, as by filter().

Call audit() again on that summary to summarize these columns further. na_count and na_rate there count the cases without a consistent value within the dispersion range, and those that a missing value leaves unknown.

Examples

# `grimmer_map_seq()` takes the same input as `grimmer_map()`:
pigs5

out <- grimmer_map_seq(
  pigs5,
  digits_x = 2,
  digits_sd = 2,
  items_averaged = 1,
  data_type = "integer",
  include_consistent = TRUE
)
out

# Case-wise summaries:
out |>
  audit()

GRIMMER-test with hypothetical group sizes

Description

Some studies report group means and standard deviations but only the total sample size, not the group sizes. For two groups, grimmer_map_total_n() tests hypothetical splits of the total, as grim_map_total_n() does.

Call audit() on the output for summary statistics.

Usage

grimmer_map_total_n(
  data,
  digits_x,
  digits_sd,
  x1 = NULL,
  x2 = NULL,
  sd1 = NULL,
  sd2 = NULL,
  n = NULL,
  ...,
  items_averaged,
  data_type,
  sd_meaning = c("sd_sample", "sd_pop", "sd_sample_or_pop", "se"),
  rounding = "up_or_down",
  threshold = 5,
  symmetric = FALSE,
  dispersion = 0:5,
  n_min = NULL,
  n_max = NULL,
  constant = NULL
)

Arguments

data

Data frame with numeric columns x1, x2, sd1, sd2, and n: the two group means, the two group SDs, and the total sample size. Which group is 1 does not matter, since both pairings with the group sizes are tested, but x1 must belong with sd1 and x2 with sd2.

digits_x, digits_sd

Integer. The number of decimal places in the means and SDs, including trailing zeros. There are no defaults, since a numeric value does not keep them (1.40 is 1.4). Either a single number for both groups, or two numbers, one per group, which apply to every row of data. If left out, they are taken from digits_x and digits_sd columns of data, with one number per row for both groups. If the decimal places differ both by group and by row, call the function on each set of rows separately and audit() each output.

x1, x2, sd1, sd2, n

Optionally, the names of the columns in data that contain these values, if they differ from the defaults.

...

These dots must be empty. Every argument after them must be named in full.

items_averaged

Integer. As in grimmer_map(): the number of items averaged into each score, given as an argument or as a column of data. There is no default.

data_type

String. As in grim(): "integer" or "continuous". Like items_averaged, give it as an argument or as a column of data; there is no default. It is returned as a column.

rounding, threshold, symmetric, sd_meaning

Passed on to grimmer(), whose section Defaults explains the defaults.

dispersion

Numeric. Steps up and down from half the total n. Default is 0:5, i.e., the even split and five steps away from it.

n_min

Numeric. Minimal group size. Default is NULL: 2 under sd_meaning = "sd_sample" or "se", which divide by n - 1, and 1 under "sd_pop" or "sd_sample_or_pop".

n_max

Numeric. Maximal group size. Default is NULL, i.e., no maximum.

constant

Optionally, values that belong to the two groups, such as their labels: a length-2 vector for the groups of x1 and x2, or a list of such vectors. Each becomes a column, as in disperse_total(), and its values stay with their groups in both directions. Default is NULL.

Value

A tibble like that of grim_map_total_n(), with x and sd columns for the reported values, dir recording which group is paired with the larger group size (the equal split of an even total appears under "forth" only), the other columns of grimmer_map() output, and the other columns of data.

Summaries with audit()

audit() on grimmer_map_total_n() output returns a tibble with one row per case and these columns:

A count is NA if it depends on a scenario that could not be decided, or on rows removed from the output, as by filter().

Call audit() again on that summary to summarize these columns further.

References

Allard, A. (2018). Analytic-GRIMMER: a new way of testing the possibility of standard deviations. https://aurelienallard.netlify.app/post/anaytic-grimmer-possibility-standard-deviations/

Bauer, P. J., & Francis, G. (2021). Expression of Concern: Is It Light or Dark? Recalling Moral Behavior Changes Perception of Brightness. Psychological Science, 32(12), 2042–2043. https://journals.sagepub.com/doi/10.1177/09567976211058727

See Also

function_map_total_n(), which created this function from grimmer_map().

Examples

df <- tibble::tribble(
  ~x1,  ~x2,  ~sd1, ~sd2, ~n,
  3.43, 5.28, 1.09, 2.12, 70,
  2.97, 4.42, 0.43, 1.65, 65
)

grimmer_map_total_n(
  df,
  digits_x = 2,
  digits_sd = 2,
  items_averaged = 1,
  data_type = "integer"
)

# `hits_total` counts the splits in which both groups are consistent:
df |>
  grimmer_map_total_n(
    digits_x = 2,
    digits_sd = 2,
    items_averaged = 1,
    data_type = "integer"
  ) |>
  audit()

# More steps away from the even split tend to find more hits:
df |>
  grimmer_map_total_n(
    digits_x = 2,
    digits_sd = 2,
    items_averaged = 1,
    data_type = "integer",
    dispersion = 0:10
  ) |>
  audit()

Visualize the standard deviations that GRIMMER admits

Description

[Experimental]

grimmer_plot_line() draws the GRIMMER test for a single value set, as grim_plot_line() does for GRIM. Of all SDs that could have been reported with digits_sd decimal places, each square is one that a sample of size n with mean x could have; the others are left out. The reported SD is marked above the row and labeled as consistent or inconsistent.

For many value sets, grim_plot_grid() accepts grimmer_map() output and shows the GRIM part of the test.

Usage

grimmer_plot_line(
  x,
  sd,
  n,
  digits_x,
  digits_sd,
  items_averaged,
  data_type,
  sd_meaning = c("sd_sample", "sd_pop", "sd_sample_or_pop", "se"),
  rounding = "up_or_down",
  threshold = 5,
  symmetric = FALSE,
  zoom = c("close", "medium", "wide"),
  color_con = "#0047ff",
  color_incon = "#ff2a00",
  color_attainable = "grey75"
)

Arguments

x, sd, n, digits_x, digits_sd, items_averaged, rounding, threshold, symmetric, sd_meaning

The same as in grimmer(), but x, sd, n, digits_x, digits_sd, and items_averaged must have length 1.

data_type

String. "integer" if the values are whole numbers, as in grimmer(). There is no default, so that every call states this assumption. "continuous" and NA are errors: if the data may be continuous, there are no values to draw.

zoom

String. How much of the row to show: "close" (the default), "medium", or "wide"; see Details.

color_con, color_incon

Strings. Color of the marker, the strip behind the squares, and the square at the reported value if it is consistent. color_con applies if the value is consistent, color_incon if not. Defaults are "#0047ff" and "#ff2a00".

color_attainable

String. Color of the squares for attainable values that were not reported, like the consistent cells of grim_plot_grid()'s raster. Default is "grey75".

Details

zoom works as in grim_plot_line(), but with zoom = "wide", the row runs from the whole number at or below sd to the next one up. It never goes below zero.

If x is GRIM-inconsistent with n, no SD is attainable and the row has no squares.

Value

A ggplot object.

Examples

# A mean of 5.23 with an SD of 2.55 is not consistent with a sample size of
# 35, but it is with 31:
grimmer_plot_line(
  x = 5.23,
  sd = 2.55,
  n = 35,
  digits_x = 2,
  digits_sd = 2,
  items_averaged = 1,
  data_type = "integer"
)
grimmer_plot_line(
  x = 5.23,
  sd = 2.55,
  n = 31,
  digits_x = 2,
  digits_sd = 2,
  items_averaged = 1,
  data_type = "integer"
)

Achievable means and SDs behind a reported pair

Description

[Experimental]

grimmer_values() returns every pair of a mean and an SD that a sample of n integers can have and that would be reported as x and sd. It is the counterpart of grim_values() for grimmer(), which is TRUE exactly if there is at least one such pair.

grimmer_closest() returns the single achievable pair whose SD is closest to sd, like grim_closest() for means. If x and sd are consistent, it is one of the pairs that grimmer_values() returns. Otherwise, it is closest among the pairs whose mean would be reported as x, or, if there are none, whose mean is the one grim_closest() returns.

Each pair comes with the whole-number sum and sum of squares behind it. Statistics computed from several groups, such as a t-value, can be compared exactly through these integers.

Usage

grimmer_values(
  x,
  sd,
  n,
  digits_x,
  digits_sd,
  items_averaged,
  data_type,
  sd_meaning = c("sd_sample", "sd_pop", "sd_sample_or_pop", "se"),
  rounding = "up_or_down",
  threshold = 5,
  symmetric = FALSE
)

grimmer_closest(
  x,
  sd,
  n,
  digits_x,
  digits_sd,
  items_averaged,
  data_type,
  sd_meaning = c("sd_sample", "sd_pop", "sd_sample_or_pop", "se"),
  rounding = "up_or_down",
  threshold = 5,
  symmetric = FALSE
)

Arguments

x

Numeric. The reported mean.

sd

Numeric. The reported standard deviation.

n

Integer. The reported sample size.

digits_x

Integer. The number of decimal places in x, including trailing zeros. There is no default, since a numeric value does not keep them (1.40 is 1.4).

digits_sd

Integer. The number of decimal places in sd, including trailing zeros. There is no default, since a numeric value does not keep them (1.40 is 1.4).

items_averaged

Integer. As in grim(), for x and sd.

data_type

String. "integer" if the values are whole numbers, as in grimmer(). There is no default, so that every call states this assumption. "continuous" is an error: such data can have any value that would be reported as the one given, so there are none to list. Under NA, a value set is undecidable unless no data can have it.

sd_meaning

String. As in grimmer(), but "sd_sample_or_pop" is not allowed: the same sum of squares gives a different SD under each premise, so call grimmer_values() once for each. Under "se", the sd column holds the standard error.

rounding

String. The rounding method assumed to have produced x. Default is "up_or_down", which accepts either direction; see Defaults in grim(). The others are "up_from_or_down_from", "ceiling_or_floor", "up", "down", "even", "up_from", "down_from", "ceiling", "floor", "trunc", "anti_trunc", "ties_up", "ties_down", "ties_away", "ties_zero", and "ties_even". See vignette("rounding-options", package = "roundwork").

threshold

Numeric. For rounding = "up_from", "down_from", or "up_from_or_down_from", the point within a step at which rounding switches direction, in tenths of a step. Default is 5. Ignored otherwise.

symmetric

Logical. If TRUE, negative numbers are rounded like their absolute values (-2.5 like 2.5) under "up", "down", and the "*_from" methods. Default is FALSE. Must not be TRUE with a "ties_*" rounding method, which already fixes the direction; see roundwork::reround().

Details

The sums are the whole numbers that grim_values() divides by n * items_averaged. For each, the sums of squares are the whole numbers within the rounding bounds of sd that n integers with that sum can have. With items_averaged > 1, the integers are the respondents' totals across the items, so the sums are those of the totals.

The number of pairs grows with n, items_averaged, and sd, and shrinks with digits_x and digits_sd. More than a million pairs for one value set is an error, since the output has one row for each. So is a sum of squares of 2^53 or more, about 9e15, which a double can't hold exactly; grimmer() tests such value sets all the same. grimmer_closest() stops at 2^48, about 3e14, beyond which it can miss the closest pair. With n from 2 to 4, both stop at 2^40, about 1.1e12, as grimmer() does; grimmer_closest() stops there even if x fails GRIM.

Under rounding = "even", the pairs include ties that base::round() may round away from x or sd, as in grim_values().

In grimmer_closest(), pairs whose SDs are equally close go by the mean closer to x, then by the smaller sum, then by the smaller SD. A negative sd is closest to the least SD there is. The number of pairs doesn't matter there, but that of the sums does: for a consistent value set, it tries one sum per remainder modulo n, and more than 100,000 of them is an error. That takes an n of more than about 10^(digits_x + 5).

Value

A tibble with one row per achievable pair. case is the index of the value set, as in grim_values(). x and sd are the achievable values, n the reported sample size, items_averaged as given, and sum and sum_squares the whole numbers behind them. An inconsistent value set has no rows, and one that cannot be decided has one row of NA but for n and items_averaged, as in grimmer().

grimmer_closest() returns the same columns with exactly one row per value set: of NA but for n and items_averaged if a value is missing or the value set cannot be decided.

See Also

grimmer(), grimmer_map(), grim_values(), and grimmer_plot_line().

Examples

# A mean of 5.23 and an SD of 2.55 are consistent with a sample size of 31,
# and there is exactly one way to get there:
grimmer_values(
  x = 5.23,
  sd = 2.55,
  n = 31,
  digits_x = 2,
  digits_sd = 2,
  items_averaged = 1,
  data_type = "integer"
)

# With one decimal place, several pairs round to the same values:
grimmer_values(
  x = 5.3,
  sd = 0.7,
  n = 30,
  digits_x = 1,
  digits_sd = 1,
  items_averaged = 1,
  data_type = "integer"
)

# None at all if the value set is inconsistent:
grimmer_values(
  x = 5.23,
  sd = 2.55,
  n = 35,
  digits_x = 2,
  digits_sd = 2,
  items_averaged = 1,
  data_type = "integer"
)

# ...but there is a pair close to it:
grimmer_closest(
  x = 5.23,
  sd = 2.55,
  n = 35,
  digits_x = 2,
  digits_sd = 2,
  items_averaged = 1,
  data_type = "integer"
)

Test whether a vector is numeric or coercible to numeric

Description

[Deprecated]

is_numeric_like() is deprecated because scrutiny no longer takes numbers as strings. It may be removed in a future version.

is_numeric_like() tests whether an object contains numbers, by scrutiny's standards:

Usage

is_numeric_like(x)

Arguments

x

Object to be tested.

Details

It asks whether a vector displays numbers, not how it is stored.

Value

Logical (length 1).

Examples

is_numeric_like(x = 1:5)
is_numeric_like(x = c(TRUE, FALSE))

is_numeric_like(x = c("42", "0.7", NA))
is_numeric_like(x = c("42", "xyz", NA))

is_numeric_like(x = as.factor(c("42", "0.7", NA)))

is_numeric_like(x = list("42", "0.7", NA))
is_numeric_like(x = list("42", "0.7", NA, c(1, 2, 3)))

# All `NA`:
is_numeric_like(x = as.character(c(NA, NA, NA)))
is_numeric_like(x = as.numeric(c(NA, NA, NA)))
is_numeric_like(x = c(NA, NA, NA))

Manage a helper column

Description

[Superseded]

manage_helper_col() is superseded by the .cols_helper argument of function_map().

A helper column is a column of a mapper's input that is not itself tested but enters the test, such as items_averaged in grim_map(). It can be given either as a column of data or as an argument of the same name. manage_helper_col() checks that the two don't disagree if both are given and, by default, adds the argument's value to data as a column if data has none.

Mappers made by function_map() call it for every .cols_helper argument. Call it yourself in a handwritten mapper, once per helper column, and reassign the result to data.

Usage

manage_helper_col(
  data,
  arg,
  default,
  add_col = TRUE,
  var_arg = deprecated(),
  affix = deprecated()
)

Arguments

data

The mapper's input data frame.

arg

The mapper argument named like the helper column.

default

The default of that argument. Leave it out if the argument has none, like items_averaged: data is then returned unchanged if the argument is missing, and the test function reports that.

add_col

Logical. If data has no such column, should one be added from arg? Default is TRUE.

var_arg, affix

[Deprecated] The old names of arg and add_col.

Value

data, possibly with the helper column added.


Extract substrings from before and inside parentheses

Description

before_parens() and inside_parens() extract the part of a string before or inside parentheses, brackets, braces, or custom delimiters. split_by_parens() does the same for the columns of a data frame.

Usage

before_parens(string, delim = "parens", sep = deprecated())

inside_parens(string, delim = "parens", sep = deprecated())

Arguments

string

String vector.

delim

String. The pair of delimiters around the second part: "parens" (the default), "brackets", or "braces", or two custom strings, matched literally.

sep

[Deprecated] The old name of delim.

Value

String vector as long as string, with surrounding whitespace removed. If a string has no opening delimiter, before_parens() returns all of it and inside_parens() returns NA. A blank part, such as the inside of "()", is NA, as is a part "NA" or "NaN".

Examples

x <- c(
  "3.72 (0.95)",
  "5.86 (2.75)",
  "3.06 (6.48)"
)

before_parens(string = x)
inside_parens(string = x)

Is a reported percentage consistent with its count?

Description

[Experimental]

percentage_of() checks whether percentage is what absolute out of n would have been reported as, given the number of decimal places and the rounding method.

This is stricter than grim() with x_meaning = "percentage", which only asks whether some count out of n produces the percentage. Here, the count is reported too.

The function is vectorized, but percentage_of_map() is more convenient for testing many cases.

Usage

percentage_of(
  absolute,
  percentage,
  n,
  digits_percentage,
  rounding = "up_or_down",
  threshold = 5,
  symmetric = FALSE
)

Arguments

absolute

Integer. The reported count.

percentage

Numeric. The reported percentage of n that absolute is said to be, e.g., 33.3.

n

Integer. The reported total.

digits_percentage

Integer. The number of decimal places in percentage, including trailing zeros. There is no default, since a numeric value does not keep them (1.40 is 1.4).

rounding

String. The rounding method assumed to have produced percentage. Default is "up_or_down", which accepts either direction. See grim() for the other methods.

threshold

Numeric. For rounding = "up_from", "down_from", or "up_from_or_down_from", the point within a step at which rounding switches direction, in tenths of a step. Default is 5. Ignored otherwise.

symmetric

Logical. If TRUE, negative numbers are rounded like their absolute values (-2.5 like 2.5) under "up", "down", and the "*_from" methods. Default is FALSE. Must not be TRUE with a "ties_*" rounding method, which already fixes the direction; see roundwork::reround().

Details

The test runs in exact integer arithmetic. absolute, percentage, n, and digits_percentage are vectorized; all other arguments must have length 1.

An absolute above n is tested like any other, as for answers to a question that allows several, whose percentages can sum to more than 100.

Value

Logical. TRUE if the three values are consistent, FALSE if not, and NA if the case cannot be decided: if a value is infinite, if absolute is not a non-negative whole number, if n is not a positive whole number, or if a missing value could decide the case either way. A missing absolute gives FALSE if no count out of n has the percentage, and a missing n gives FALSE if no n makes absolute that percentage. A missing digits_percentage is treated as in grim().

Causes of inconsistencies

A FALSE result means that the three values cannot all be correct as reported, but not why. The cause is often trivial: the percentage may be based on another denominator than n, such as the number of valid responses, or on an unconventional rounding method. percentage_of_map_seq() with var = "n" shows which nearby denominators would make the percentage consistent.

See Also

grim() with x_meaning = "percentage", for a percentage without a count.

Examples

# 40 out of 120 is 33.33...%, so it can be
# reported as 33 or 33.3, but not as 34:
percentage_of(40, 33, 120, digits_percentage = 0)
percentage_of(40, 33.3, 120, digits_percentage = 1)
percentage_of(40, 34, 120, digits_percentage = 0)

Test many reported percentages against their counts

Description

[Experimental]

percentage_of_map() applies percentage_of() to every row of a data frame with counts, percentages, and totals.

Call audit() on the output for summary statistics. See percentage_of() for causes of inconsistencies.

Usage

percentage_of_map(
  data,
  digits_percentage,
  absolute = NULL,
  percentage = NULL,
  n = NULL,
  ...,
  rounding = "up_or_down",
  threshold = 5,
  symmetric = FALSE
)

Arguments

data

Data frame with columns absolute, percentage, and n. Any other columns are returned alongside the results.

digits_percentage

Integer. The number of decimal places in percentage, including trailing zeros. There is no default, since a numeric value does not keep them (1.40 is 1.4). Either a single number for the whole column or one number per row of data. If left out, it is taken from a digits_percentage column of data.

absolute, percentage, n

Optionally, the names of the columns in data that contain these values, if they differ from the defaults.

...

These dots must be empty. Every argument after them must be named in full.

rounding, threshold, symmetric

The same as in grim().

Value

A tibble with the columns absolute, percentage, n, digits_percentage, consistency, and any other columns from data. It has the class scrutiny_percentage_of_map, which is recognized by audit().

Summaries with audit()

audit() on percentage_of_map() output returns a tibble with one row and these columns:

  1. all_cases: total number of value sets.

  2. incon_cases: number of inconsistent value sets.

  3. emp_incon: proportion of inconsistent value sets.

Examples

df <- tibble::tibble(
  absolute = c(40, 37, 25),
  percentage = c(33, 31, 20),
  n = 120
)

df |>
  percentage_of_map(digits_percentage = 0)

Test percentages against their counts with values around the reported ones

Description

[Experimental]

percentage_of_map_seq() varies the reported counts, percentages, and totals a few steps up and down and applies percentage_of() to each resulting value set. This shows whether a small error in one of them could explain an inconsistency.

Call audit() on the output for summary statistics.

Usage

percentage_of_map_seq(
  data,
  digits_percentage,
  absolute = NULL,
  percentage = NULL,
  n = NULL,
  ...,
  rounding = "up_or_down",
  threshold = 5,
  symmetric = FALSE,
  var = c("absolute", "percentage", "n"),
  dispersion = 1:5,
  out_min = "auto",
  out_max = "auto",
  include_reported = FALSE,
  include_consistent = FALSE
)

Arguments

data

A data frame that percentage_of_map() could take.

digits_percentage

Integer. The number of decimal places in percentage, including trailing zeros. There is no default, since a numeric value does not keep them (1.40 is 1.4). Either a single number for the whole column or one number per row of data. If left out, it is taken from a digits_percentage column of data. It also sets the step size for dispersing percentage.

absolute, percentage, n

Optionally, the names of the columns in data that contain these values, if they differ from the defaults.

...

These dots must be empty. Every argument after them must be named in full.

rounding, threshold, symmetric

The same as in grim().

var

String. The variables to disperse. Default is c("absolute", "percentage", "n").

dispersion

Numeric. Steps up and down from the reported values, in units of the last decimal place. Default is 1:5.

out_min, out_max

Limits on the dispersed values. Both default to "auto", which keeps absolute and percentage at 0 or above and n at 1 or above. A number applies to every dispersed variable, and NULL removes the limit.

include_reported

Logical. Should the reported values themselves be part of the sequences? Default is FALSE.

include_consistent

Logical. Should consistent value sets be dispersed as well, not just inconsistent ones? Default is FALSE.

Value

A tibble with the columns of percentage_of_map() output plus diff_var, case, and var, as in grim_map_seq(). As there, the other columns of data are dropped.

Examples

df <- tibble::tibble(
  absolute = c(40, 37, 25),
  percentage = c(33, 31, 20),
  n = 120
)

df |>
  percentage_of_map_seq(digits_percentage = 0) |>
  audit()

Means and sample sizes for GRIM-testing

Description

A fictional dataset with means and sample sizes of flying pigs. For demonstrating grim_map() and functions built on it.

Usage

pigs1

Format

A tibble (data frame) with 12 rows and 2 columns. The columns are:

x

Numeric. Means.

n

Numeric. Sample sizes.

See Also

pigs2 for GRIM-testing percentages instead of means, pigs3 for DEBIT-testing, pigs4 for detecting duplicates, and pigs5 for GRIMMER-testing.


Percentages and sample sizes for GRIM-testing

Description

A fictional dataset with percentages and sample sizes of flying pigs. For demonstrating grim_map() and functions built on it, with x_meaning = "percentage".

Usage

pigs2

Format

A tibble (data frame) with 6 rows and 2 columns. The columns are:

x

Numeric. Percentages.

n

Numeric. Sample sizes.

See Also

pigs1 for GRIM-testing means instead of percentages, pigs3 for DEBIT-testing, pigs4 for detecting duplicates, and pigs5 for GRIMMER-testing.


Binary means and standard deviations for DEBIT-testing

Description

A fictional dataset with means and standard deviations from a binary distribution related to flying pigs. For demonstrating debit_map() and functions built on it.

Usage

pigs3

Format

A tibble (data frame) with 7 rows and 3 columns. The columns are:

x

Numeric. Means.

sd

Numeric. Standard deviations.

n

Numeric. Sample sizes.

See Also

pigs1 for GRIM-testing means, pigs2 for GRIM-testing percentages instead of means, pigs4 for detecting duplicates, and pigs5 for GRIMMER-testing.


Data with duplications

Description

A fictional dataset with observations of flying pigs. It contains multiple duplicates. For demonstrating the ⁠duplicate_*()⁠ functions, such as duplicate_count().

Usage

pigs4

Format

A tibble (data frame) with 5 rows and 3 columns, describing various body measures of the fictional pigs. The columns are:

snout

Numeric. Snout width.

tail

Numeric. Tail length.

wings

Numeric. Wingspan.

See Also

pigs1 for GRIM-testing means, pigs2 for GRIM-testing percentages, pigs3 for DEBIT-testing, and pigs5 for GRIMMER-testing.


Means, SDs, and sample sizes for GRIMMER-testing

Description

A fictional dataset with means, standard deviations (SDs), and sample sizes of flying pigs. For demonstrating grimmer_map() and functions built on it.

Usage

pigs5

Format

A tibble (data frame) with 12 rows and 3 columns. The columns are:

x

Numeric. Means.

sd

Numeric. Standard deviations.

n

Numeric. Sample sizes.

See Also

pigs1 for GRIM-testing the same means, with other sample sizes, pigs2 for GRIM-testing percentages instead of means, pigs3 for DEBIT-testing, and pigs4 for detecting duplicates.


Restore trailing zeros

Description

[Deprecated]

These functions are deprecated because the tests no longer take strings, so there is no need to restore trailing zeros for them. Give the number of decimal places as digits_x or digits_sd instead. To format numbers for display, use formatC(x, format = "f", digits = 2).

restore_zeros() formats numbers as strings with a given number of decimal places, padding with zeros. By default, the target is the number of decimal places of the value that has the most. Do not rely on this default without checking it, since the value with the most decimal places may itself have lost trailing zeros.

restore_zeros_df() applies it to the columns of a data frame, by default all those coercible to numeric.

Usage

restore_zeros(
  x,
  width = NULL,
  sep_in = ".",
  sep_out = sep_in,
  check_width = c("capped", "never")
)

restore_zeros_df(
  data,
  cols = everything(),
  check_numeric_like = TRUE,
  check_decimals = FALSE,
  width = NULL,
  sep_in = ".",
  sep_out = sep_in,
  check_width = c("capped", "never")
)

Arguments

x

Numeric, or a string coercible to numeric. A value that is not coercible, such as "5%", becomes NA with a warning, and so does one with an exponent so large that it can't be written out, such as "1e-999999". A blank string or "NA" becomes NA without a warning.

width

Integer. Number of decimal places the output should have, up to 100,000. Length 1 or the length of x. Default is NULL, the largest number of decimal places in x.

sep_in

String. The separator between the integer part and the decimal places in the input, matched literally. Default is ".".

sep_out

String. The separator in the output. Default is sep_in.

check_width

String (length 1). What to do if a value has more decimal places than width? "capped" (the default) throws an error; "never" lets it pass.

data

Data frame or matrix. Only in restore_zeros_df().

cols

Columns to operate on, selected with tidyselect. Default is everything(), restricted by check_numeric_like. Only in restore_zeros_df().

check_numeric_like

Logical. If TRUE (the default), only columns that is_numeric_like() accepts are processed. A column that isn't numeric-like gets a warning if cols is given and selects it, or if it is processed anyway, unless it is empty. Only in restore_zeros_df().

check_decimals

Logical. If TRUE, columns without any decimal places are skipped. Default is FALSE. Only in restore_zeros_df().

Details

Displayed numbers are often rounded, so a value may have more decimal places than it shows. x |> decimal_places() |> max() gives the actual maximum.

As in decimal_places(), numeric values with more than 15 significant digits cannot be padded accurately; enter such numbers as strings.

Value

Examples

vec <- c(212, 75.38, 4.9625)

# By default, pad to the most decimal places in `vec`:
restore_zeros(vec)

# Or to a given width:
restore_zeros(vec, width = 6)

iris <- tibble::as_tibble(iris)

# All numeric columns, but not the factor:
restore_zeros_df(iris)

# Selected columns:
restore_zeros_df(iris, starts_with("Sepal"), width = 3)

Reconstruct the input of a sequence mapper

Description

reverse_map_seq() takes the output of a function made by function_map_seq() and reconstructs the tested columns of the cases it dispersed, without ⁠digits_*⁠. audit() builds on it for sequence output.

Usage

reverse_map_seq(data)

Arguments

data

Data frame with the "scrutiny_map_seq" class.

Value

A tibble with the tested columns, one row per case in data.

Examples

pigs1

out <- grim_map_seq(
  pigs1,
  digits_x = 2,
  items_averaged = 1,
  data_type = "integer",
  include_consistent = TRUE
)

reverse_map_seq(out)

Reconstruct the input of a total-n mapper

Description

reverse_map_total_n() takes the output of a function made by function_map_total_n() and reconstructs the reported columns of the data frame it was called on. audit() builds on it for total-n output.

Usage

reverse_map_total_n(data)

Arguments

data

Data frame with the "scrutiny_map_total_n" class.

Value

A tibble with the reported columns, such as x1, x2, and n, one row per case.

Examples

df <- tibble::tribble(
  ~x1,  ~x2,  ~n,
  3.43, 5.28, 90,
  2.97, 4.42, 103
)

out <- grim_map_total_n(
  df,
  digits_x = 2,
  items_averaged = 1,
  data_type = "integer"
)

reverse_map_total_n(out)

Turn row values into column names

Description

Tables extracted from PDFs, e.g. with tabulapdf, sometimes have their column names in the first row (or rows) and nondescript names above. row_to_colnames() replaces the column names by the values of those rows and drops the rows.

Usage

row_to_colnames(data, row = 1L, collapse = " ", drop = TRUE)

Arguments

data

Data frame or matrix.

row

Integer. Position of the row or rows that contain the column names. Default is 1.

collapse

String. Separator between the values of several rows. Default is " ".

drop

Logical. Should the rows be removed? Default is TRUE.

Details

With several rows, their values are pasted together per column. Missing values, blank strings, "NA", and "NaN" are skipped, and whitespace around a value is trimmed; if all of a column's values in these rows are missing or blank, it keeps its name. The new names must be unique: a header that repeats, as in "M, SD, M, SD", is an error unless a second row tells the columns apart. A label that spans several columns, such as a group name above "M, SD", is often only in the first of them; repeat it in each one first.

Value

data with new column names, as a data frame of the same class, or a tibble if data was a matrix.

See Also

unheadr::mash_colnames(), a more general solution to the same problem.


Read the settings a mapper recorded on its output

Description

[Experimental]

Every mapper, such as grim_map(), records the settings of the call that produced its output. scrutiny_meta() returns them as a tibble, so that a report can state the rounding method or SD convention behind a result.

Settings are the arguments that apply to the whole call, with defaults resolved, such as rounding, x_meaning, or sd_meaning. Arguments that may vary by row, such as digits_x and items_averaged, are columns of the output instead, even if given as a single value. Sequence and total-n mappers, such as grim_map_seq(), add their own settings, such as dispersion.

Printed output of grim_map(), grimmer_map(), debit_map(), or percentage_of_map(), including sequence and total-n output, has a header line that names the settings that are not at their defaults, as in ⁠# GRIM: rounding = "up"⁠, or says ⁠default settings⁠.

Some dplyr verbs, such as summarise(), drop the record, and with it the header line; filter(), arrange(), and mutate() keep it.

Usage

scrutiny_meta(data)

Arguments

data

Output of a mapper function.

Value

A tibble with one row per setting and these columns, or NULL if data has no record of its settings:

Examples

pigs1 |>
  grim_map(
    digits_x = 2,
    items_averaged = 1,
    data_type = "integer",
    rounding = "up"
  ) |>
  scrutiny_meta()

# Only the settings that are not at their defaults:
pigs1 |>
  grim_map_seq(
    digits_x = 2,
    items_averaged = 1,
    data_type = "integer",
    dispersion = 1:10
  ) |>
  scrutiny_meta() |>
  dplyr::filter(!default)

Standard deviation of binary data

Description

Compute the SD of binary data (0 and 1 values) from different inputs:

These are the SDs that debit() tests under the same sd_meaning.

[Superseded] sd_binary_0_n() and sd_binary_1_n() are superseded by sd_binary_groups(): sd_binary_0_n(group_0, n) is sd_binary_groups(group_0, n - group_0).

Usage

sd_binary_groups(
  group_0,
  group_1,
  sd_meaning = c("sd_sample", "sd_pop", "sd_sample_or_pop", "se")
)

sd_binary_0_n(
  group_0,
  n,
  sd_meaning = c("sd_sample", "sd_pop", "sd_sample_or_pop", "se")
)

sd_binary_1_n(
  group_1,
  n,
  sd_meaning = c("sd_sample", "sd_pop", "sd_sample_or_pop", "se")
)

sd_binary_mean_n(
  x,
  n,
  sd_meaning = c("sd_sample", "sd_pop", "sd_sample_or_pop", "se"),
  mean = deprecated()
)

Arguments

group_0

Integer. Count of 0s.

group_1

Integer. Count of 1s.

sd_meaning

String. What to compute: the sample SD, "sd_sample" (the default); the population SD, "sd_pop"; or the standard error of the mean, "se", as in debit(). "sd_sample_or_pop" is an error here, since it stands for two premises with different values.

n

Integer. Total sample size.

x

Numeric. Mean of the data, i.e., the share of 1s.

mean

[Deprecated] The old name of x.

Value

Numeric. The SD or standard error; NA where n or a count is not a whole number, and where n is below 2 under "sd_sample" and "se", which divide by n - 1.

References

Heathers, James A. J., and Brown, Nicholas J. L. 2019. DEBIT: A Simple Consistency Test For Binary Data. https://osf.io/5vb3u/.

Examples

# 127 values are 0 and 153 are 1, so n = 280:
sd_binary_groups(group_0 = 127, group_1 = 153)
sd_binary_0_n(group_0 = 127, n = 280)
sd_binary_1_n(group_1 = 153, n = 280)

# From the mean, given enough decimal places:
sd_binary_mean_n(x = 0.5464286, n = 280)

# The population SD and the standard error:
sd_binary_groups(group_0 = 127, group_1 = 153, sd_meaning = "sd_pop")
sd_binary_groups(group_0 = 127, group_1 = 153, sd_meaning = "se")

Is a vector a certain kind of sequence?

Description

[Deprecated]

These functions are deprecated because scrutiny no longer uses them: the sequence mappers record how far each value was moved, in diff_var. They may be removed in a future version.

Predicates for numeric vectors, or vectors coercible to numeric:

By default (test_linear = TRUE), the last three also test for linearity.

Usage

is_seq_linear(x, tolerance = .Machine$double.eps^0.5)

is_seq_ascending(x, test_linear = TRUE, tolerance = .Machine$double.eps^0.5)

is_seq_descending(x, test_linear = TRUE, tolerance = .Machine$double.eps^0.5)

is_seq_dispersed(
  x,
  from,
  test_linear = TRUE,
  tolerance = .Machine$double.eps^0.5
)

Arguments

x

Numeric, character, or factor vector. Strings and factor labels count as numbers as determined by is_numeric_like(); any that are not make the result FALSE. A logical vector is accepted only if all its values are NA. Any other type, such as a list or a data frame, is an error.

tolerance

Numeric. Tolerance for comparing values. Default is about 1.5e-8, as in dplyr::near(). It is never finer than the rounding error of arithmetic on the values, so 0 allows that error only.

test_linear

Logical. Should linearity be tested as well? Default is TRUE. Not in is_seq_linear().

from

Numeric, character, or factor, like x. Only in is_seq_dispersed(). The value x should be centered on.

Value

Logical (length 1). If x contains NA:

A missing from is handled the same way.

See Also

validate::is_linear_sequence(), which is more permissive with NA and supports date-times.

Examples

is_seq_linear(x = 3:7)
is_seq_linear(x = c(3:7, 9))

# The other predicates test for linearity by default:
is_seq_ascending(x = c(2, 7, 9))
is_seq_ascending(x = c(2, 7, 9), test_linear = FALSE)

is_seq_descending(x = c(9, 7, 2))
is_seq_descending(x = c(9, 7, 2), test_linear = FALSE)

is_seq_dispersed(x = c(2, 3, 5, 7, 8), from = 5)
is_seq_dispersed(x = c(2, 3, 5, 7, 8), from = 5, test_linear = FALSE)

# These fail their own test regardless of linearity:
is_seq_ascending(x = c(1, 7, 4), test_linear = FALSE)
is_seq_descending(x = c(9, 15, 3), test_linear = FALSE)
is_seq_dispersed(1:10, from = 5, test_linear = FALSE)

Sequences dispersed around a value

Description

seq_disperse() creates a sequence of values around from, a given number of steps up and down, where a step is one unit of the last decimal place: 7.93 is surrounded by 7.91, 7.92, 7.94, 7.95, and so on.

seq_disperse_df() returns the sequence as a data frame column, with further columns added as in tibble::tibble(). Its arguments are those of seq_disperse() with a leading dot.

Usage

seq_disperse(
  from,
  by = NULL,
  dispersion = 1:5,
  offset_from = 0L,
  out_min = "auto",
  out_max = NULL,
  include_reported = TRUE,
  track_diff_var = FALSE
)

seq_disperse_df(
  .from,
  .by = NULL,
  ...,
  .dispersion = 1:5,
  .offset_from = 0L,
  .out_min = "auto",
  .out_max = NULL,
  .include_reported = TRUE,
  .track_diff_var = FALSE
)

Arguments

from, .from

Numeric, or a string coercible to numeric. The value to disperse from.

by, .by

Numeric. Step size. Default is NULL, in which case it is one unit of the last decimal place of from. A number does not keep trailing zeros, so 5.30 steps by 0.1: give by = 0.01, or from as the string "5.30".

dispersion, .dispersion

Numeric. Whole numbers of steps up and down from from. Default is 1:5.

offset_from, .offset_from

Integer. Moves the starting point by that many steps. Default is 0.

out_min, .out_min, out_max, .out_max

Limits on the values, including from itself. out_min defaults to "auto", one step above zero, which suits a count but not a quantity that can reach zero or be negative; use NULL for an unbounded quantity such as a mean, or 0 for one bounded at zero such as a standard deviation. out_max defaults to NULL, no maximum, and "auto" is the same.

include_reported, .include_reported

Logical. Should from itself be part of the sequence? Default is TRUE.

track_diff_var, .track_diff_var

Logical. Should the number of steps from from be returned as well? Default is FALSE. If TRUE, seq_disperse() returns a list of the sequence and the steps, and seq_disperse_df() adds a diff_var column.

...

Further columns, added as in tibble::tibble(). Only in seq_disperse_df().

Details

Unlike base::seq(), these functions need not return a regular sequence: dispersion can be any non-negative whole numbers, in any order, such as c(1, 4, 9). The values follow that order, mirrored around from. Steps of 0 and repeated steps are dropped.

Value

See Also

disperse() and base::seq(), whose ideas seq_disperse() combines.

Examples

seq_disperse(from = 4.02)

# A number loses trailing zeros, so state the step size:
seq_disperse(from = 5.30, by = 0.01)

# More steps:
seq_disperse(from = 4.02, dispersion = 1:10)

# Irregular steps:
seq_disperse(from = 4.02, dispersion = c(2, 3, 7))

# The data frame variant takes further columns:
seq_disperse_df(.from = 4.02, n = 45)

Split columns by parentheses, brackets, braces, or similar

Description

Summary statistics are often reported like "2.65 (0.27)". split_by_parens() splits such columns into the part before the parentheses and the part inside them. By default, it splits every column that contains the delimiters. Set transform to TRUE to pivot the result into a longer format.

Usage

split_by_parens(
  data,
  cols = everything(),
  check_delim = TRUE,
  keep = FALSE,
  transform = FALSE,
  delim = "parens",
  end1 = "x",
  end2 = "sd",
  check_sep = deprecated(),
  sep = deprecated()
)

Arguments

data

Data frame or matrix.

cols

Columns to split, selected with tidyselect. Default is everything(), restricted by check_delim.

check_delim

Logical. If TRUE (the default), columns whose values do not all contain the delimiters are skipped. Missing values, blank strings, "NA", and "NaN" don't count. If cols is given, each column skipped causes a warning. Under check_delim = FALSE, so does each column split although it lacks the delimiters, unless all its values are missing.

keep

Logical. Should the original columns be kept? Default is FALSE.

transform

Logical. Should the output be pivoted into a longer format, with one column per part and an .origin column naming the source? Default is FALSE. Requires keep = FALSE and every column to be split.

delim

String. The pair of delimiters around the second part: "parens" (the default), "brackets", or "braces", or two custom strings, matched literally.

end1, end2

Strings. Suffixes of the two columns that a split column produces. Defaults are "x" and "sd".

check_sep, sep

[Deprecated] The old names of check_delim and delim.

Value

An ungrouped data frame: a tibble unless data is a plain data frame and transform is FALSE.

See Also

Examples

df1 <- tibble::tribble(
  ~drone,           ~selfpilot,
  "0.09 (0.21)",    "0.19 (0.13)",
  "0.19 (0.28)",    "0.53 (0.10)",
  "0.62 (0.16)",    "0.50 (0.11)",
  "0.15 (0.35)",    "0.57 (0.16)",
)

df1 |>
  split_by_parens()

# Only some columns:
df1 |>
  split_by_parens(cols = drone)

# Longer format:
df1 |>
  split_by_parens(transform = TRUE)

# Other suffixes:
df1 |>
  split_by_parens(end1 = "beta", end2 = "se")

# Other delimiters:
df2 <- tibble::tribble(
  ~drone,           ~selfpilot,
  "0.09 [0.21]",    "0.19 [0.13]",
  "0.19 [0.28]",    "0.53 [0.10]",
)

df2 |>
  split_by_parens(delim = "brackets")

df3 <- tibble::tribble(
  ~drone,           ~selfpilot,
  "0.09 <0.21>",    "0.19 <0.13>",
  "0.19 <0.28>",    "0.53 <0.10>",
)

df3 |>
  split_by_parens(delim = c("<", ">"))