Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

doc tweaks and improvements #16

Merged
merged 2 commits into from
Aug 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/Project.toml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
[deps]
BoxCox = "1248164d-f7a6-4bdb-8e8d-8c4a187b3ce6"
CairoMakie = "13f3f980-e62b-5c42-98c6-ff1f3baf88f0"
DocStringExtensions = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae"
Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
Expand Down
34 changes: 14 additions & 20 deletions docs/src/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,17 @@ We start with the square root of a normal distribution.
using BoxCox
using CairoMakie
using Random
CairoMakie.activate!(; type="svg")

x = abs2.(randn(MersenneTwister(42), 1000))
let f = Figure(; resolution=(400, 400))
let f = Figure()
ax = Axis(f[1,1]; xlabel="x", ylabel="density")
density!(ax, x)
f
end
```

```@example Unconditional
let f = Figure(; resolution=(400, 400))
ax = Axis(f[1,1]; xlabel="theoretical", ylabel="observed")
ax = Axis(f[1,2]; xlabel="theoretical quantiles", ylabel="observed values")
qqnorm!(ax, x)
colsize!(f.layout, 1, Aspect(1, 1.0))
colsize!(f.layout, 2, Aspect(1, 1.0))
resize_to_layout!(f)
f
end
```
Expand All @@ -44,19 +43,14 @@ Note that the resulting transform isn't exactly a square root, even though our d
Now that we've fit the transform, we use it like a function to transform the original data.

```@example Unconditional
let f = Figure(; resolution=(400, 400))
let f = Figure(), bcx = bc.(x)
ax = Axis(f[1,1]; xlabel="x", ylabel="density")
density!(ax, bc.(x))
f
end
```

There is also a special method for `qqnorm` provided for objects of type `BoxCoxTransformation`, which shows the QQ plot of the transformation applied to the original data.

```@example Unconditional
let f = Figure(; resolution=(400, 400))
ax = Axis(f[1,1]; xlabel="theoretical", ylabel="observed")
qqnorm!(ax, bc)
density!(ax, bcx)
ax = Axis(f[1,2]; xlabel="theoretical quantiles", ylabel="observed values")
qqnorm!(ax, bcx; qqline=:fitrobust)
colsize!(f.layout, 1, Aspect(1, 1.0))
colsize!(f.layout, 2, Aspect(1, 1.0))
resize_to_layout!(f)
f
end
```
Expand Down
15 changes: 12 additions & 3 deletions docs/src/mixed-models.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,13 @@ bc = fit(BoxCoxTransformation, model)

## Choosing an appropriate transformation

Although we receive a single "best" value (approximately -1.0747) the fitting process, it is worthwhile to look at the profile likelihood plot for the transformation:
Although we receive a single "best" value (approximately -1.0747) from the fitting process, it is worthwhile to look at the profile likelihood plot for the transformation:

```@example Mixed
boxcoxplot(bc; conf_level=0.95)
```

Here we see that -1 is nearly as good. Moreover, ``\text{time}^-1`` has a natural interpretation as *speed*.
Here we see that -1 is nearly as good. Moreover, time``^{-1}`` has a natural interpretation as *speed*.
In other words, we can model reaction speed instead of reaction time.
Then instead of seeing whether participants take longer to respond with each passing day, we can see whether their speed increases or decreases.
In both cases, we're looking at whether they respond *faster* or *slower* and even the terminology *fast* and *slow* suggests that speed is easily interpretable.
Expand All @@ -69,12 +69,21 @@ While useful at times, speed has a natural interpretation and so we instead use

## Fitting a model to the transormed response

Because `reaction` is stored in milliseconds, we use `1000 / reaction` instead of `1 / reaction` so that our speed units are responses per second.

```@example Mixed
model_bc = fit(MixedModel,
@formula(1 / reaction ~ 1 + days + (1 + days | subj)),
@formula(1000 / reaction ~ 1 + days + (1 + days | subj)),
dataset(:sleepstudy))
```

For our original model on the untransformed scale, the intercept was approximately 250, which means that the average response time was about 250 milliseconds.
For the model on the speed scale, we have an intercept about approximately 4, which means that the average response speed is about 4 responses per second, which implies that the the average response time is 250 milliseconds.
In other words, our new results are compatible with our previous estimates.

!!! note
Because the Box-Cox transformation helps a model achieve normality of the *residuals*, it helps fulfill the model assumptions. When these assumptions are not fulfilled, we may still get similar estimates, but the standard errors and derived measures (e.g., confidence intervals and associated coverage) may not be correct.

Finally, let's take a look at our the residual diagnostics for our transformed and untransformed models:

## Impact of transformation
Expand Down
Loading