Skip to content

Commit

Permalink
doc tweaks and improvements (#16)
Browse files Browse the repository at this point in the history
* clean  up the presentation of unconditional

* mixed models tweaks
  • Loading branch information
palday authored Aug 31, 2023
1 parent 29731f4 commit b830442
Show file tree
Hide file tree
Showing 3 changed files with 27 additions and 23 deletions.
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

2 comments on commit b830442

@palday
Copy link
Owner Author

@palday palday commented on b830442 Aug 31, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JuliaRegistrator
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Error while trying to register: "Tag with name v0.2.0 already exists and points to a different commit"

Please sign in to comment.