The dots of a Dot + Error figure can be joined by a line, under Plot options. A set of estimates along a time axis is usually read for which way it went, and the line is what says so. It is computed by the same summary as the points it joins -- the same helper at the same confidence level -- so it passes through them rather than near them, and it is drawn beneath them. One line per colour when there is a grouping variable, and per panel.
Survey weights. A numeric column can be set as the survey weight under
Describe. Each row then counts for as many people as its weight:
histograms, densities, boxplots and violins are drawn through ggplot2's
weight aesthetic, and a LOWESS smoother is fitted with the weights. A
dotplot draws one dot per row and cannot be weighted, so it is refused with
the reason rather than drawn unweighted.
A weighted figure reports two counts wherever it reports one -- in the title,
on every panel strip, in the Strata tab and in the number-at-risk table:
N, the rows the figure is drawn from, and the weighted N, the sum of their
weights. They answer different questions, and neither can be read off the
other. The minimum N per figure is still a number of rows.
The bar on a weighted Dot + Error figure and the band on a weighted
Kaplan-Meier curve are design-based, estimated with the 'survey' package,
which is now a dependency. The design takes the weight and, optionally, the
sampling strata and the clusters (primary sampling units), with a
cluster ID read within its stratum (nest = TRUE). A mean gets its standard
error or its t interval; a proportion gets svyciprop()'s Korn-Graubard
interval (in place of Clopper-Pearson) or its Wilson interval. The curve is
svykm(), and its band is drawn on the log scale as confint() draws it.
The design is built once, over the whole sample, before any row is set aside for a layer, and every panel and figure is a subpopulation of it. Its standard errors therefore count every stratum and cluster in the sample -- including the clusters it holds none of -- rather than those of a design rebuilt on its own rows, and its intervals are read on the whole design's degrees of freedom. The generated script builds the design in the same place.
A stratum that holds a single cluster has no variance to estimate, and a figure that needs one is refused with the stratum named and a suggestion to merge it, rather than failing inside 'survey'. A row with no stratum or cluster is excluded and counted, as a row with no weight is.
epi_cohort gains svy_weight, a survey weight that reads the cohort as a
sample of about 50,000 admissions with the severe presentations
over-sampled. The other twelve columns are unchanged.
A smoother's band is not offered while a weight is set. loess() reads a
weight as the precision of an observation, not as the number of people it
stands for, so the band would not be a design-based interval.
A row with no weight is excluded and counted, in the same place and the same way as a row with no value for a layer variable.
Derive a variable gains a fifth method: time resolution. A date or a date-time is read at whatever resolution the question is asked at, and the answer is a variable that can be used as a layer like any other. A column of admission dates has as many values as there are days and cannot be stratified on at all; read by season it has four.
The resolutions come in two families, because they answer different questions. A calendar period -- year, quarter, month, week, day, hour, minute -- keeps the year, so every row in March 2021 lands together and the values still run along real time. That matters for a trend: the column stays a date, so a figure drawn against it is spaced by elapsed time rather than by level number, and its axis can still be given date ticks. A position in the cycle -- month of the year, season, quarter of the year, day of the week, hour of the day -- drops the year and pools every March since the data began, which is what a seasonal pattern is.
The months of the year read January to December and the days of the week Monday to Sunday. Sorting them would put April first and Friday second, which is never what was meant, so the levels are written out rather than derived.
A season's start month is chosen rather than assumed. The four seasons follow it three months at a time, so March gives the northern hemisphere and September the southern; the order spring, summer, fall, winter does not move with it.
A resolution a column cannot answer is refused with the reason, not with the nearest available message. An hour of the day taken from a plain date would be midnight for every row -- the underlying function answers rather than refusing -- and a month taken from a time of day has no calendar to fall in.
A Line plot drawn against a date or a date-time can say how far apart its
ticks are and what each one reads. The values plotted do not change; only the
ticks over them, so a column that really holds dates can be shown as a run of
years. With no axis label typed, the axis is titled by the unit -- Year
rather than the column's name -- since that is what the ticks now say.
A number typed into X axis from or X axis to while the X variable is a date no longer stops the figure. It could not have worked -- those boxes are numeric and hold no date -- but 'ggplot2' does not ignore a number handed to a date scale, it refuses to draw at all. The range is now dropped and the figure appears.
A time-of-day column stored as data.table::ITime is no longer offered as a
continuous measurement or as a Kaplan-Meier follow-up time. It is a count of
seconds past midnight and so reads as a number, which is not a reason to plot
a clock on an axis of measurements.
The name suggested for a derived variable now says which resolution it is
(admit_date_season, not admit_date_cat), and the box is refreshed only
when it still holds a name this application put there. It used to be
refreshed whenever the name merely looked like one, which a real column
called birth_year or treatment_cat does.
The bar on a Dot + Error figure can say what it stands for. It has always been one standard error either side of the mean, which describes how precisely the mean is known and is about half the width a reader takes an error bar to have. A confidence interval is now offered beside it, at 90, 95 or 99 percent.
The mean's interval uses the t distribution, because the standard error is
itself estimated from the same observations. It is the interval t.test()
reports and the one Hmisc::smean.cl.normal() computes, written out in the
generated script so that running that script needs no further package.
A Y variable coded 0 and 1 is a proportion rather than a measurement, and it gets its own two intervals: exact (Clopper-Pearson), which inverts the binomial test and so never covers less often than it claims, and Wilson, which inverts the score test and is the better-centred of the two at small n. Neither runs outside 0 and 1, and neither collapses to nothing in a stratum where no one had the outcome -- which is where a mean and a standard error are at their most misleading and where a descriptive figure most needs to be honest.
Which of the two to prefer is a judgement rather than a fact, so both are offered and neither is the default.
A 0/1 variable is now offered as the Y variable of a Dot + Error figure. It was not offered at all before -- having two values is exactly what rules a column out as a measurement -- so the one figure that can describe a proportion was the one figure that could not be pointed at one.
The error bars offered follow the Y variable that is chosen. A measurement is offered the standard error and the mean's interval; a 0/1 outcome is offered the two proportion intervals and nothing else. Neither set applies to the other, so the list is narrowed rather than shown in full and half of it refused afterwards.
An outcome is accepted however it is written: 0 and 1, TRUE and FALSE, a
two-level factor, or two character values. Most people have already made
theirs a factor or a logical, so requiring 0 and 1 would have meant recoding
a column to get at a figure describing it.
Which of the two values counts as the outcome having happened is a control
rather than an assumption. It defaults to the second -- the reference level
first, the level being modelled second, as glm() reads a factor -- because
a factor declared c("Yes", "No") means the opposite of one declared the
other way round and nothing in the data says which was intended.
The counting is written into the figure, as y = as.integer(outcome == "Died"), rather than done to the data beforehand. 'ggplot2' would otherwise
put a non-numeric Y on a discrete scale and hand the interval the level
number -- 1 and 2 -- which comes back as a proportion above one with a NaN
as the only sign that anything is wrong. Doing it in the aes() keeps the
column as it was typed and puts what was counted on the screen; the Y axis
is labelled Proportion outcome = Died to match.
A declared factor level that nobody had still counts as one of the two, so a stratum where the outcome never happened is a proportion of 0 with a real interval around it rather than a missing figure.
Tick labels on either axis can be turned, by 30, 45, 60 or 90 degrees. Twelve month names along one axis do not fit side by side, and 'ggplot2' overlaps them rather than dropping any. The turn is emitted after the theme, since a theme replaces the element rather than merging into it.
The bundled epi_cohort gains admit_date, over 2021 to 2023. The
seasonality is in the case mix rather than in the number of admissions:
moderate and severe presentations cluster in the winter and mild ones spread
into the summer, so a season read off it carries a real difference in crp
and in los_days. The other eleven columns are unchanged, values included.
A numeric variable is now treated as continuous because of its type, not because it has enough distinct values. Counting distinct values guessed wrong on measurements that are recorded coarsely -- an age in whole years over a narrow range, a visit number, a score out of five -- and refused to plot them on an axis. A column holding at most two values is still not offered as a measurement: an indicator coded 0/1 cannot describe a spread. A few-levelled numeric column can now be both, plotted on an axis and stratified by.
A line plot's Mark the observations option has moved from Plot
options to Describe, beside the rest of the line settings, and draws
its points at size = 1 so that a dense set of them does not cover the
line it belongs to.
Derive a variable (the panel formerly called Categorize) gains a fourth method: missing vs observed. It reads no values, only whether there is a value, so it applies to a variable of any type -- a factor, a character column or a date as much as a number -- and turns it into two groups that can be used as a layer like any other categorical variable.
Rows with a missing value in a layer variable are excluded, as before. A
missing-vs-observed variable is the one layer that never has one: is.na()
answers for every row. The rows another layer would have dropped are
therefore exactly the ones this puts in front of you, which is what makes it
a way to look at who is not in the data rather than only to count them.
Describing a variable within its own missingness leaves the Missing figure with nothing to draw. The app reports that arrangement under Layers instead of drawing an empty figure without comment, and still draws it.
A variable with no missing values, or with nothing but missing values, gives one group rather than two and is refused with a note saying which.
Initial release.
ggstratify() takes a data frame or a matrix that is already in your
session, and nothing else. There is no file-upload control in the
application and a file path is no longer accepted as dataset. Data read
from disk is typed by whatever the reader guessed, and would then be
described on those guesses; read the file yourself, look at the result, and
pass the object.
The name the data was passed under is what the generated code refers to it
by, so ggstratify(cohort) prints code that starts d <- cohort.
Previews are subsampled only above one million rows for scatter and line plots and ten million for the plot types that summarise their rows first, and never for Kaplan-Meier curves.