Ami a NumPy-ból hiányzik: Cramer-féle V

Az előző blogbejegyzésemben A Cramer-féle asszociációs együtthatót mutattam be egy egyszerű példán keresztül. A NumPy alapból nem tartalmazza ezt a számítást, így implementáltam egy megoldást, hogy bemutathassam a NumPy komolyabb szintű használatát is….


This content originally appeared on DEV Community and was authored by Programozás és elemzés Olivérrel

Az előző blogbejegyzésemben A Cramer-féle asszociációs együtthatót mutattam be egy egyszerű példán keresztül. A NumPy alapból nem tartalmazza ezt a számítást, így implementáltam egy megoldást, hogy bemutathassam a NumPy komolyabb szintű használatát is. A NumPy alapokról itt olvashatsz a blogomon.

Cramer-V NumPy implementáció

Az adatszerkezet értelmezése

Ahhoz, hogy értsd az adatszerkezetben lévő adatokat, mindenképpen érdemes nézni a hivatkozott blogbejegyzést, mivel a számokat a blogbejegyzésben lévő példából vettem. Az alap egy 2x4-es mátrix (NumPy array). Fontos, hogy összegezni kellene a sorokat, és az oszlopokat, hogy megkapjuk az úgynevezett kontingenciatáblázatot még, ha részletekben is. (Külön az eredeti, külön az összesített adatok.)

table = np.array([
    [6,4],
    [6,4],
    [7,5],
    [35,32]
])

Sorok és oszlopok összegzése

Az alábbi kódblokkban a táblázat sorainak és oszlopainak összegzése látható. Ehhez a sum metódust alkalmaztam. Az axis nevesített paraméter az összegzés tengelyét jelenti. Az 1-es a sorokat összegzi, míg a 0-ás az oszlopokat.

row_totals = table.sum(axis=1)
col_totals = table.sum(axis=0)
N = table.sum()

Ezek az értékek fognak kijönni:
rows_total = [10, 10, 12, 67]
cols_total = [54, 45]
A végeredmény két vektor lesz a sorok és oszlopok összegzéséről. Kiszámoltam még az értékek összegét is, amelyet az N változó tárol. Erre a későbbiek során szükségünk lesz.

Diadikus szorzat (outer product)

A diadikus szorzat, vagy angolul outer product kiszámítása során az egyik vektort transzponáljuk (elforgatjuk 90 fokkal). Így lehet elképzelni:

a‾⋅b‾T \underline{a} \cdot \underline{b}^T abT

Látszik, hogy az alábbi példában az elso sor elso oszlop eleme 10*54=540 lett. Az első sor második oszlop eleme 10*45=450. Tehát egyszerűen az egyik vektor értékeit minden másik vektorbeli értékkel összeszorozzuk. Az eredmény ebben a konkrét esetben 2x4, azaz 8 elem lesz.

54 45
10 540 450
10 540 450
12 648 540
67 3618 3015

Független értékek előállítása

Az alábbi kód előállítja azokat az értékeket, amelyek a függetlenséghez szükségesek. Az outer egyszerűen csak a két vektor összes lehetséges szorzatát adja vissza. Ha belegondolsz a műveletek sorrendje mindegy is. Lényegtelen, hogy először szorzunk, aztán osztunk le, vagy fordítva. Az ne tévesszen meg, hogy egy sorban vannak a műveletek! Valójában számonként megy végbe a szorzás és az osztás is, nem aggregálás történik.

expected = np.outer(row_totals, col_totals) / N

Az eredmény valahogyan így fog kinézni:

54 45
10 540/99 = 5.45 450/99 = 4.55
10 540/99 = 5.45 450/99 = 4.55
12 648/99 = 6.55 540/99 = 5.45
67 3618/99 = 36.55 3015/99 = 30.45

χ2 érték kiszámolása

Az alábbi sorral fogjuk kiszámolni χ2 értékét. A sum metódussal összegezzük is egyből a kalkulált értékeket. Ha nem emlékeznél a képletre, akkor az így néz ki:

χ2=Σ(fij−f∗ij)2f∗ij \chi^2 = \Sigma \frac{(f_{ij} - {f^\ast}_{ij})^2}{f^\ast \footnotesize{ij}} χ2=Σfij(fijfij)2



Itt pedig a kód:

chi2 = ((table - expected) ** 2 / expected).sum()

Cramer-féle asszociációs együttható

Most már csak egyetlen lépés van hátra, a Cramer-féle együttható kalkulációja. A table.shape visszaadja a táblázat sorainak és oszlopainak számát.

r, c = table.shape
np.sqrt(chi2 / (N * min(r - 1, c - 1)))

A teljes számítás függvény alakban

A függvény kialakítása során hibakezelést is végeztem. Ahhoz, hogy egy táblázat kontingenciatáblázat legyen, és több ismérv szerint tudjunk kapcsolatot számolni, nyilván szükséges, hogy mind a sorok, mind az oszlopok száma legalább kettő legyen.

if r < 2 or c < 2:
        raise ValueError("Cramer's V requires at least a 2x2 contingency table.")

Ha bármelyik érték negatív a táblázatban, akkor teljesen értelmetlenek az adatok, hiszen itt gyakoriságokról van szó.

if np.any(table < 0):
        raise ValueError("Contingency table cannot contain negative values.")

Az alábbi kódblokkban azt ellenőrzöm, hogy az értékek összege kisebb-e, vagy egyenlő, mint nulla. Nulla csak akkor lehet, hogyha minden érték nulla, a negatív számokat pedig alapból kiszűrtük, tehát negatív érték itt elő sem fordulhatna. Elméletben elég lenne annyit ellenőrizni hogy N értéke egyenlő-e nullával. Ettől függetlenül szerintem maradhat a megoldás. Neked mi a véleményed erről a kérdésről?

if N <= 0:
        raise ValueError("Total count must be greater than zero.")

Az utolsó ellenőrzés azt hivatott kiszűrni, hogy a számolt várható értékek (függetlenséghez szükséges értékek) között ne legyen nulla. Ez akkor következhet be, hogyha egy-egy sorban, vagy oszlopban csak nullás értékek találhatók az eredeti táblázatban. Mivel ilyenkor nullosztó hibát kapnánk, ezért dobunk kivételt.

if np.any(expected == 0):
        raise ValueError("Expected frequencies contain zeros. Cannot compute chi-square.")

A teljes kód pedig itt olvasható:

import numpy as np

def cramers_v(table):
    r, c = table.shape

    if r < 2 or c < 2:
        raise ValueError("Cramer's V requires at least a 2x2 contingency table.")

    if np.any(table < 0):
        raise ValueError("Contingency table cannot contain negative values.")

    row_totals = table.sum(axis=1)
    col_totals = table.sum(axis=0)
    N = table.sum()

    if N <= 0:
        raise ValueError("Total count must be greater than zero.")

    expected = np.outer(row_totals, col_totals) / N

    if np.any(expected == 0):
        raise ValueError("Expected frequencies contain zeros. Cannot compute chi-square.")

    chi2 = ((table - expected) ** 2 / expected).sum()

    return np.sqrt(chi2 / (N * min(r - 1, c - 1)))


This content originally appeared on DEV Community and was authored by Programozás és elemzés Olivérrel


Print Share Comment Cite Upload Translate Updates
APA

Programozás és elemzés Olivérrel | Sciencx (2026-04-23T15:32:15+00:00) Ami a NumPy-ból hiányzik: Cramer-féle V. Retrieved from https://www.scien.cx/2026/04/23/ami-a-numpy-bol-hianyzik-cramer-fele-v/

MLA
" » Ami a NumPy-ból hiányzik: Cramer-féle V." Programozás és elemzés Olivérrel | Sciencx - Thursday April 23, 2026, https://www.scien.cx/2026/04/23/ami-a-numpy-bol-hianyzik-cramer-fele-v/
HARVARD
Programozás és elemzés Olivérrel | Sciencx Thursday April 23, 2026 » Ami a NumPy-ból hiányzik: Cramer-féle V., viewed ,<https://www.scien.cx/2026/04/23/ami-a-numpy-bol-hianyzik-cramer-fele-v/>
VANCOUVER
Programozás és elemzés Olivérrel | Sciencx - » Ami a NumPy-ból hiányzik: Cramer-féle V. [Internet]. [Accessed ]. Available from: https://www.scien.cx/2026/04/23/ami-a-numpy-bol-hianyzik-cramer-fele-v/
CHICAGO
" » Ami a NumPy-ból hiányzik: Cramer-féle V." Programozás és elemzés Olivérrel | Sciencx - Accessed . https://www.scien.cx/2026/04/23/ami-a-numpy-bol-hianyzik-cramer-fele-v/
IEEE
" » Ami a NumPy-ból hiányzik: Cramer-féle V." Programozás és elemzés Olivérrel | Sciencx [Online]. Available: https://www.scien.cx/2026/04/23/ami-a-numpy-bol-hianyzik-cramer-fele-v/. [Accessed: ]
rf:citation
» Ami a NumPy-ból hiányzik: Cramer-féle V | Programozás és elemzés Olivérrel | Sciencx | https://www.scien.cx/2026/04/23/ami-a-numpy-bol-hianyzik-cramer-fele-v/ |

Please log in to upload a file.




There are no updates yet.
Click the Upload button above to add an update.

You must be logged in to translate posts. Please log in or register.