Transforming Data to Normality in R: Log, Square-Root & Inverse

Fix non-normal, skewed data so parametric tests apply — measure skewness, pick the right transformation, and confirm it worked

Biostatistics

Learn how to transform skewed data to normality in R so parametric tests (t-test, ANOVA) apply. Measure skewness with the moments package, choose the right transformation (square-root, log or inverse, for positive or negative skew), and confirm the result with density plots and the before/after skewness.

Published

June 23, 2026

Modified

July 7, 2026

TipKey takeaways
  • Parametric tests (the t-test, ANOVA) assume an approximately normal outcome; when it is skewed, a transformation can fix it.
  • Measure skewness with moments::skewness(): 0 = symmetric, positive = right-skewed, negative = left-skewed; the larger the magnitude, the worse the departure.
  • Match the transformation to the severity: square-root (moderate), log (greater), inverse 1/x (severe) — with a mirrored form for negative skew.
  • Confirm it worked: re-plot the density against the normal curve and re-compute skewness.
  • Transformation complicates interpretation — only do it when needed, and with large samples the central limit theorem often makes it unnecessary.

Introduction

Parametric methods such as the t-test and ANOVA assume the outcome is approximately normally distributed in each group. When the normality check fails because the data are skewed, transforming the variable can pull it back toward normal so the parametric test becomes valid.

Skewness measures the asymmetry of a distribution. A positively skewed (right-skewed) variable has a long right tail (Mode < Median < Mean); a negatively skewed (left-skewed) one has a long left tail. The sign and size of the skewness coefficient tell you the direction and severity — and therefore which transformation to reach for. This lesson works through it on the built-in USJudgeRatings data.

Measure skewness

The moments package computes the skewness coefficient — here for iris$Sepal.Length:

library(moments)

skewness(iris$Sepal.Length, na.rm = TRUE)
[1] 0.3117531

A small positive value (~0.3) means a mild right skew — close enough to symmetric.

Transformation methods

Pick the transformation by the severity of the skew, and use the mirrored form for negative skew (where max(x + 1) - x flips the tail before transforming):

Severity Positive (right) skew Negative (left) skew
Moderate sqrt(x) sqrt(max(x + 1) - x)
Greater log10(x) log10(max(x + 1) - x)
Severe 1 / x 1 / (max(x + 1) - x)
Warning

For a log transformation, every value must be positive — add a constant first if the data contain zeros or negatives.

Worked example: two skewed variables

We use USJudgeRatings and two of its columns: CONT (lawyer–judge contacts, positively skewed) and PHYS (physical ability, negatively skewed).

library(moments)

df <- USJudgeRatings
c(CONT = skewness(df$CONT), PHYS = skewness(df$PHYS))
     CONT      PHYS 
 1.085972 -1.558215 

CONT has skewness +1.09 (right-skewed) and PHYS −1.56 (left-skewed) — both well away from zero. Plot their densities against the normal curve to see it:

library(ggpubr)

df <- USJudgeRatings

ggarrange(
  ggdensity(df, x = "CONT", fill = "#3a86d4", title = "CONT (right-skewed)") +
    stat_overlay_normal_density(color = "red", linetype = "dashed"),
  ggdensity(df, x = "PHYS", fill = "#3a86d4", title = "PHYS (left-skewed)") +
    stat_overlay_normal_density(color = "red", linetype = "dashed"),
  ncol = 2
)

Two density plots: CONT is clearly right-skewed and PHYS is left-skewed, each compared to the overlaid dashed normal curve.

Apply a log10 transformation — the plain form for the right-skewed CONT, the mirrored form for the left-skewed PHYS:

library(ggpubr)

df <- USJudgeRatings
df$CONT <- log10(df$CONT)                       # positive skew: plain log
df$PHYS <- log10(max(df$PHYS + 1) - df$PHYS)    # negative skew: mirrored log

ggarrange(
  ggdensity(df, x = "CONT", fill = "#3a86d4", title = "CONT (log10)") +
    stat_overlay_normal_density(color = "red", linetype = "dashed"),
  ggdensity(df, x = "PHYS", fill = "#3a86d4", title = "PHYS (mirrored log10)") +
    stat_overlay_normal_density(color = "red", linetype = "dashed"),
  ncol = 2
)

Density plots of CONT and PHYS after a log10 transformation, each overlaid with the normal curve; both are much closer to normal than before.

Confirm with the skewness of the transformed variables:

library(moments)

df <- USJudgeRatings
df$CONT <- log10(df$CONT)
df$PHYS <- log10(max(df$PHYS + 1) - df$PHYS)
c(CONT = skewness(df$CONT), PHYS = skewness(df$PHYS))
     CONT      PHYS 
0.6555572 0.5824358 

Both skews shrink in magnitude: CONT’s right skew eases (+1.09 → +0.66), and PHYS’s strong left skew drops to a mild right skew (−1.56 → +0.58) — much closer to symmetric, though the mirrored log slightly over-corrects PHYS past zero. Transformation reduces the skew but does not always land exactly on normal, which is why you re-check rather than assume.

Try it live

Transform a skewed variable yourself and watch the skewness drop. The sandbox boots on first Run.

🟢 With an AI agent

Ask Prova “my outcome is skewed and fails the normality test — measure its skewness and suggest the right transformation, then check whether it worked” — it answers with code you can run on your own data. The runtime is the judge. Ask Prova →

Common issues

You logged data containing zeros or negatives. log10(0) is -Inf and log10() of a negative is NaN. Add a constant so all values are positive first (e.g. log10(x + 1)).

You used the positive-skew formula on negatively skewed data. Negative (left) skew needs the mirrored form transform(max(x + 1) - x), which flips the long tail before transforming. Check the sign of skewness() first.

The transformation didn’t help — or you don’t want to interpret transformed units. Transformation isn’t guaranteed to work, and it complicates interpretation (a difference in log10 units, not raw units). With samples larger than ~30–40 the central limit theorem often lets parametric tests cope with mild non-normality, so transforming may be unnecessary — run the test on both and compare.

Frequently asked questions

Parametric tests (t-test, ANOVA, regression) assume an approximately normal outcome. When the data are skewed enough to violate that, a transformation (log, square-root, inverse) can reshape the distribution toward normal so the parametric test’s p-values are trustworthy — an alternative to switching to a non-parametric test.

Match it to the severity of the skew: square-root for moderate, log for greater, and inverse (1/x) for severe skew. Use the plain form for positive (right) skew and the mirrored form (transform(max(x + 1) - x)) for negative (left) skew. Check the result by re-computing skewness() and re-plotting against the normal curve.

Skewness measures asymmetry: 0 is perfectly symmetric, a positive value means a long right tail (right-skewed), and a negative value means a long left tail (left-skewed). The larger the magnitude, the further from normal — values beyond about ±1 indicate substantial skew worth addressing.

No. Transformation complicates interpretation and isn’t always successful. For the t/F family of tests, moderate non-normality is often tolerable, and with samples larger than ~30–40 the central limit theorem usually makes parametric tests valid anyway. Transform only when the violation is real and the test requires it — or consider a non-parametric alternative instead.

Test your understanding

  1. Run it. In the live cell, compute the skewness of USJudgeRatings$CONT before and after a log10 transformation. Fill the blank. Did the skewness move toward 0?
  2. Conceptual. A variable has skewness −1.8 (strongly left-skewed). Which transformation form do you apply, and why the mirrored version?

Fill the blank with log10. Compare the two skewness values. For question 2, recall that negative skew needs transform(max(x + 1) - x) to flip the long tail before transforming.

x <- USJudgeRatings$CONT
skewness(x)          #> ~1.09 (right-skewed)
skewness(log10(x))   #> ~0.66 — closer to 0

The log10 transformation reduced the skew. For question 2: a strongly left-skewed variable uses a mirrored transformation such as log10(max(x + 1) - x) — the mirror reflects the long left tail to the right so the log can compress it, which a plain log10(x) would not do.

Conclusion

You can now transform skewed data to normality in R: measure the skew with moments::skewness(), match a square-root, log or inverse transformation to its severity (mirrored for negative skew), and confirm with density plots and the recomputed skewness. Use it to rescue a parametric test when the normality assumption fails — but remember it complicates interpretation, and large samples often make it unnecessary.

Reuse

Citation

BibTeX citation:
@online{2026,
  author = {},
  title = {Transforming {Data} to {Normality} in {R:} {Log,}
    {Square-Root} \& {Inverse}},
  date = {2026-06-23},
  url = {https://www.datanovia.com/learn/biostatistics/assumptions/data-transformation-in-r},
  langid = {en}
}
For attribution, please cite this work as:
“Transforming Data to Normality in R: Log, Square-Root & Inverse.” 2026. June 23. https://www.datanovia.com/learn/biostatistics/assumptions/data-transformation-in-r.