Cramer's V calculator
Cramer's V summarizes the strength of association between two categorical variables on a scale from zero to one.
Run — free
Enter observed frequencies as a contingency-table matrix and the calculator derives Pearson's chi-square statistic, then normalizes it for the table's effective dimensions and total count. The result is useful when a chi-square test tells you that variables are related but you also need a comparable effect-size measure. The response includes the intermediate statistic and dimensions so the calculation remains easy to inspect.
Prepare the contingency table correctly
Arrange the observed frequencies as a rectangular matrix. Each row should represent one category of the first variable, and each column should represent one category of the second variable. A cell contains the number, or nonnegative weighted frequency, of observations that belong to that row-column combination. Do not send percentages from independently normalized rows unless those percentages truly act as comparable weights, because Cramer's V depends on the marginal totals as well as the pattern inside the table. Every row must have the same number of cells, and the matrix needs at least two rows and two columns. Values must be finite and nonnegative. The calculator permits fractional values for weighted contingency tables, but ordinary survey or event data will usually use whole-number counts. Entirely empty categories are harmless: an all-zero row or column is excluded from the effective dimensions because it carries no information and would have an expected frequency of zero. However, the grand total cannot be zero, and at least two nonempty rows and columns must remain.
Understand the calculation
The calculator first adds every row and column and finds the grand total. For each active cell, its expected frequency under independence is the row total multiplied by the column total and divided by the grand total. Pearson's chi-square statistic is the sum of the squared difference between observed and expected frequency, divided by that expected frequency. Cramer's V then normalizes chi-square using the grand total and the smaller of the row degrees and column degrees: the square root of chi-square divided by total times min(rows minus one, columns minus one). That normalization makes association strengths easier to compare across differently sized tables than raw chi-square values. The output includes `cramers_v`, `chi_square`, `total`, `rows`, and `columns`. Rows and columns report the nonempty dimensions actually used. A value near zero indicates little departure from independence, while a value near one indicates a strong categorical association. The exact practical meaning still depends on the subject, sample design, and category structure.
Interpret the result without overstating it
Treat Cramer's V as an effect-size summary, not as proof of causation and not as a replacement for a significance test. A large sample can produce a statistically significant chi-square result for a weak association, while a small sample can leave an important-looking pattern uncertain. Review the cell counts, expected frequencies, sampling method, and domain context alongside V. Common verbal labels such as weak, moderate, or strong are only rough conventions; useful thresholds can vary with the number of categories and the consequences of the decision. Sparse tables deserve particular care because the usual chi-square approximation may be unreliable when expected counts are small. In those cases, consider combining defensible categories, collecting more observations, or using an exact or simulation-based test before drawing inferential conclusions. Also remember that a single V value does not identify which cells drive the relationship. Inspect standardized residuals or the observed-versus-expected table when you need direction and detail. Use this calculator for a transparent, reproducible strength measure, then report the table, sample size, chi-square statistic, and study assumptions with it.
What you can do with it
Compare survey responses
Measure how strongly response categories are associated with groups such as region, cohort, or channel.
Evaluate experiment outcomes
Summarize the relationship between a categorical treatment assignment and a categorical outcome after building the count table.
Monitor product segments
Track association strength between customer segments and behaviors without relying on raw chi-square values alone.
FAQ
What does the calculation cost?
It is free to run in the browser on this page, and an API request costs $0.002.
What range does Cramer's V use?
It normally ranges from 0 for no observed association to 1 for the strongest possible association given the table.
Can the table contain decimal values?
Yes. Finite, nonnegative fractional frequencies are accepted, which supports appropriately constructed weighted tables.
What happens with an all-zero row or column?
It is excluded from the effective dimensions because it contributes no observations. At least two nonempty rows and two nonempty columns must remain.
Why does a zero-total table produce an error?
Expected frequencies and the normalization both divide by the grand total, so Cramer's V is undefined when every cell is zero.
Does Cramer's V prove that one variable causes the other?
No. It describes association strength in the supplied table and does not establish direction or causality.
For developers — API access
Everything on this page is available programmatically. This section is for teams who want to wire it into their own systems; everyone else can just use the tool above.
API endpoint
Prefer to automate it? One authenticated POST creates the task; the result comes back by webhook or a signed link. The same capability also runs here on the web, by email and from Telegram — and soon from our app too.
Call it from your stack
curl -X POST https://api.kit.forhosting.com/stat/cramers-v \
-H "Authorization: Bearer $KIT_KEY" \
-H "Content-Type: application/json" \
-d '{"table":[[20,30],[30,20]]}'const res = await fetch("https://api.kit.forhosting.com/stat/cramers-v", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.KIT_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
"table": [
[
20,
30
],
[
30,
20
]
]
})
});
const { task_id } = await res.json();import os, requests
res = requests.post(
"https://api.kit.forhosting.com/stat/cramers-v",
headers={"Authorization": f"Bearer {os.environ['KIT_KEY']}"},
json={
"table": [
[
20,
30
],
[
30,
20
]
]
},
)
task_id = res.json()["task_id"]<?php
$res = file_get_contents("https://api.kit.forhosting.com/stat/cramers-v", false, stream_context_create([
"http" => [
"method" => "POST",
"header" => "Authorization: Bearer " . getenv("KIT_KEY") . "\r\nContent-Type: application/json",
"content" => '{"table":[[20,30],[30,20]]}',
],
]));
$task = json_decode($res, true);body := bytes.NewBufferString(`{"table":[[20,30],[30,20]]}`)
req, _ := http.NewRequest("POST", "https://api.kit.forhosting.com/stat/cramers-v", body)
req.Header.Set("Authorization", "Bearer "+os.Getenv("KIT_KEY"))
req.Header.Set("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)Example request
{
"table": [
[
20,
30
],
[
30,
20
]
]
}Example response
{
"task_id": "tsk_a1b2c3d4e5f6a1b2c3d4e5f6",
"type": "stat.cramers_v",
"status": "queued",
"_links": {
"result": "/tasks/tsk_…/result"
}
}The API is asynchronous: the call returns a task_id immediately and the result arrives by webhook. Polling is capped at 1 req/s per task.
Pricing
Published price — no tokens, no invented credits. A failed task is never charged.
Errors
| HTTP | Code | Meaning |
|---|---|---|
401 | unauthorized | Missing or invalid API key. |
402 | insufficient_balance | Your balance doesn't cover the task price. |
404 | unknown_type | That task type doesn't exist. |
429 | rate_limited | Too many requests. Use the webhook instead of polling. |