diff --git a/bootstrap-t.ipynb b/bootstrap-t.ipynb index 16a57d2..184f9f0 100644 --- a/bootstrap-t.ipynb +++ b/bootstrap-t.ipynb @@ -2,47 +2,30 @@ "cells": [ { "cell_type": "markdown", - "metadata": { - "collapsed": false - }, + "metadata": {}, "source": [ - "Bootstrapowa wersja testu t.\n", - "Implementacja powinna obejmować test dla jednej próby, dla dwóch prób niezależnych oraz dla dwóch prób zależnych.\n", - "W każdej sytuacji oczekiwanym wejście jest zbiór danych w odpowiednim formacie, a wyjściem p-wartość oraz ostateczna decyzja.\n", - "Dodatkowo powinien być rysowany odpowiedni rozkład statystyki testowej." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "collapsed": false - }, - "source": [ - "Zbiór danych - ???\n", - "Hipoteza zerowa - ???\n", - "Hipoteza alternatywna - ???\n", + "# Projekt - Test t studenta\n", "\n", - "Dla każdego z 3 testów inne\n", - "https://www.jmp.com/en_ch/statistics-knowledge-portal/t-test.html" + "- Marcin Kostrzewski\n", + "- Krystian Wasilewski\n", + "- Mateusz Tylka\n", + "\n", + "## Test t studenta\n", + "\n", + "Metoda statystyczna służącą do porównania dwóch średnich między sobą gdy znamy liczbę badanych próbek, średnią arytmetyczną oraz wartość odchylenia standardowego lub wariancji.\n", + "Jest to jeden z mniej skomplikowanych i bardzo często wykorzystywanych testów statystycznych używanych do weryfikacji hipotez. Dzięki niemu możemy dowiedzieć się czy dwie różne średnie są różne niechcący (w wyniku przypadku) czy są różne istotnie statystycznie (np. z uwagi na naszą manipulację eksperymentalna).\n", + "Wyróżniamy 3 wersję testu t:\n", + "\n", + "1. test t Studenta dla jednej próby\n", + "2. test t Studenta dla prób niezależnych\n", + "3. test t Studenta dla prób zależnych\n", + "\n", + "Wszystkie rodzaje testów są testami parametrycznymi, a co za tym idzie nasze mierzone zmienne ilościowe powinny mieć rozkład normalny." ] }, { "cell_type": "code", - "execution_count": 155, - "outputs": [], - "source": [ - "# TODO: Poprzestawiać kolejność definicji funkcji?" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } - }, - { - "cell_type": "code", - "execution_count": 156, + "execution_count": 48, "metadata": { "pycharm": { "name": "#%%\n" @@ -62,7 +45,110 @@ }, { "cell_type": "code", - "execution_count": 157, + "execution_count": 49, + "metadata": {}, + "outputs": [], + "source": [ + "dataset = pd.read_csv('experiment_data.csv')" + ] + }, + { + "cell_type": "code", + "execution_count": 50, + "metadata": {}, + "outputs": [], + "source": [ + "def calculate_p(t_stat, df):\n", + " \"\"\"Funkcja oblicza wartość *p* na podstawie statystyki testowej i stopni swobody\"\"\"\n", + " return (1.0 - t.cdf(abs(t_stat), df)) * 2.0" + ] + }, + { + "cell_type": "code", + "execution_count": 51, + "metadata": {}, + "outputs": [], + "source": [ + "def calculate_cv(df, alpha=0.05):\n", + " \"\"\"Funkcja oblicza wartość krytyczną (critical value)\"\"\"\n", + " return t.ppf(1.0 - alpha, df)" + ] + }, + { + "cell_type": "code", + "execution_count": 53, + "metadata": { + "pycharm": { + "name": "#%%\n" + } + }, + "outputs": [], + "source": [ + "def t_test(sample_1, sample_2=None, df_fn=df_single, t_stat_fn=t_stat_single, population_mean=None, alpha=0.05):\n", + " \"\"\"\n", + " Funkcja przeprowadza test T-studenta dla dwóch zmiennych.\n", + " liczba kolumn wynosi 1, test jest przeprowadzany dla jednej zmiennej.\n", + " @param df_fn - funkcja obliczająca stopnie swobody\n", + " @param t_stat_fn - funkcja obliczająca statystykę T\n", + " \"\"\"\n", + " t_stat_list = get_t_stats(sample_1, sample_2, t_stat_fn, population_mean=population_mean)\n", + " t_stat_sum = sum(t_stat_list)\n", + "\n", + " data_size = sample_1.shape[0]\n", + "\n", + " t_stat = t_stat_sum / data_size\n", + " # TODO: dolna i górna opcja dają inne wyniki z jakiegoś powodu (???)\n", + " t_stat = mean(t_stat_list)\n", + "\n", + " if sample_2 is None:\n", + " df = df_fn(sample_1)\n", + " else:\n", + " df = df_fn(sample_1, sample_2)\n", + " cv = calculate_cv(df, alpha)\n", + " p = calculate_p(t_stat, df)\n", + " return t_stat, df, cv, p, t_stat_list" + ] + }, + { + "cell_type": "code", + "execution_count": 54, + "metadata": {}, + "outputs": [], + "source": [ + "def get_t_stats(sample_1, sample_2=None, t_stat_fn=t_stat_single, population_mean=None):\n", + " \"\"\"Funkcja oblicza listę statystyk testowych dla każdej próbki bootstrapowej wybranej na podstawie danych sample_1 i sample_2\"\"\"\n", + " t_stat_list = []\n", + "\n", + " # One sample test\n", + " if t_stat_fn==t_stat_single:\n", + " if not population_mean:\n", + " raise Exception(\"population_mean not provided\")\n", + " for bootstrap in generate_bootstraps(sample_1):\n", + " stat = t_stat_fn(bootstrap, population_mean)\n", + " t_stat_list.append(stat)\n", + " return t_stat_list\n", + "\n", + " # Two sample test\n", + " for bootstrap_1, bootstrap_2 in zip(generate_bootstraps(sample_1), generate_bootstraps(sample_2)):\n", + " stat = t_stat_fn(bootstrap_1, bootstrap_2)\n", + " t_stat_list.append(stat)\n", + " return t_stat_list" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Testowanie hipotez metodą bootstrap\n", + "\n", + "**Bootstrap** – metoda szacowania (estymacji) wyników poprzez wielokrotne losowanie ze zwracaniem z próby. Polega ona na utworzeniu nowego rozkładu wyników, na podstawie posiadanych danych, poprzez wielokrotne losowanie wartości z posiadanej próby. Metoda ze zwracaniem polega na tym, że po wylosowaniu danej wartości, “wraca” ona z powrotem do zbioru.\n", + "\n", + "Metoda bootstrapowa znajduje zastosowanie w sytuacji, w której nie znamy rozkładu z populacji z której pochodzi próbka lub w przypadku rozkładów małych lub asymetrycznych. W takim wypadku, dzięki tej metodzie, wyniki testów parametrycznych i analiz opartych o modele liniowe są bardziej precyzyjne. Zazwyczaj losuje się wiele próbek, np. 2000 czy 5000." + ] + }, + { + "cell_type": "code", + "execution_count": 55, "metadata": { "pycharm": { "name": "#%%\n" @@ -77,9 +163,20 @@ " yield data.iloc[indices, :]" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Test t studenta dla jednej próby\n", + "\n", + "**Test t Studenta dla jednej próby** wykorzystujemy gdy chcemy porównać średnią “teoretyczną” ze średnią, którą faktycznie możemy zaobserwować w naszej bazie danych. Średnia teoretyczna to średnia pochodząca z innych badań lub po prostu bez większych uzasadnień pochodząca z naszej głowy.\n", + "\n", + "Wyobraźmy sobie, że mamy dane z takimi zmiennymi jak wzrost pewnej grupy ludzi. Dzięki testowi t Studenta dla jednej próby możemy dowiedzieć się np. czy wzrost naszego młodszego brata wynoszący 155cm odbiega znacząco od średniej wzrostu tej grupy. Hipoteza zerowa w takim badaniu wyglądałaby następująco H0: Badana próba została wylosowana z populacji, w której wzrost osób wynosi średnio 155cm. Z kolei hipoteza alternatywna będzie brzmiała H1: Badana próba nie została wylosowana z populacji gdzie średni wzrost wynosi 155cm\n" + ] + }, { "cell_type": "code", - "execution_count": 158, + "execution_count": 60, "metadata": { "collapsed": false, "pycharm": { @@ -92,11 +189,88 @@ " \"\"\"Funkcja oblicza wartość statystyki testowej dla jednej próbki\"\"\"\n", " if sample.empty:\n", " raise Exception(\"Empty sample\")\n", - " sample = sample[0].values.tolist()\n", + " sample = sample['Height'].values.tolist()\n", " sample_size = len(sample)\n", " return (mean(sample) - population_mean) / (stdev(sample) / sqrt(sample_size))" ] }, + { + "cell_type": "code", + "execution_count": 57, + "metadata": {}, + "outputs": [], + "source": [ + "def df_single(sample_1):\n", + " \"\"\"Funkcja oblicza stopnie swobody dla jednej próbki\"\"\"\n", + " # TODO: I have no clue what to return from here\n", + " return len(sample_1)" + ] + }, + { + "cell_type": "code", + "execution_count": 58, + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + }, + "outputs": [], + "source": [ + "def bootstrap_one_sample(sample, population_mean):\n", + " return t_test(\n", + " sample_1=sample,\n", + " df_fn=df_single,\n", + " t_stat_fn=t_stat_single,\n", + " population_mean=population_mean\n", + " )" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Sprawdzenie czy osoba o wzroście 165cm pasuje do populacji (nie jest odmieńcem)" + ] + }, + { + "cell_type": "code", + "execution_count": 61, + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "t: 6.854929920812628, df: 500, cv: 1.6479068539295045, p: 2.1091128843409024e-11\n", + "\n" + ] + } + ], + "source": [ + "#TODO: poprawić kod aby można było podawać kolumny\n", + "\n", + "t_stat, df, cv, p, _ = bootstrap_one_sample(dataset, 165)\n", + "pretty_print_full_stats(t_stat, df, cv, p)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Test t studenta dla prób niezależnych\n", + "\n", + "**Test t Studenta dla prób niezależnych** jest najczęściej stosowaną metodą statystyczną w celu porównania średnich z dwóch niezależnych od siebie grup. Wykorzystujemy go gdy chcemy porównać dwie grupy pod względem jakiejś zmiennej ilościowej. Na przykład gdy chcemy porównać średni wzrost kobiet i mężczyzn w danej grupie.\n", + "\n", + "Zazwyczaj dwie średnie z różnych od siebie grup będą się różnić. Test t Studenta powie nam jednak czy owe różnice są istotne statystycznie – czy nie są przypadkowe. Hipoteza zerowa takiego testu będzie brzmiała H0: Średni wzrost w grupie mężczyzn jest taki sam jak średni w grupie kobiet. Hipoteza alternatywna z kolei H1: Kobiety będą różnić się od mężczyzn pod wzrostu.\n", + "Jeśli wynik testu t Studenta będzie istotny na poziomie p < 0,05 możemy odrzucić hipotezę zerową na rzecz hipotezy alternatywnej.\n" + ] + }, { "cell_type": "code", "execution_count": 159, @@ -118,6 +292,57 @@ " return (mean(sample_1) - mean(sample_2)) / sed" ] }, + { + "cell_type": "code", + "execution_count": 162, + "metadata": {}, + "outputs": [], + "source": [ + "def df_ind(sample_1, sample_2):\n", + " \"\"\"Funkcja oblicza stopnie swobody dla dwóch próbek niezależnych\"\"\"\n", + " return len(sample_1) + len(sample_2) - 2" + ] + }, + { + "cell_type": "code", + "execution_count": 167, + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + }, + "outputs": [], + "source": [ + "def bootstrap_independent(sample_1, sample_2):\n", + " return t_test(\n", + " sample_1=sample_1,\n", + " sample_2=sample_2,\n", + " df_fn=df_ind,\n", + " t_stat_fn=t_stat_ind\n", + " )" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# TODO: Wyciągnąć wysokości kobiet i mężczyzn oraz poprawić kod aby można było podawać kolumny\n", + "t_stat, df, cv, p, _ = bootstrap_independent(dataset, dataset)\n", + "pretty_print_full_stats(t_stat, df, cv, p)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Test t studenta dla prób zależnych\n", + "\n", + "W odróżnieniu od testu t – Studenta dla prób niezależnych, gdzie porównujemy dwie grupy, ten rodzaj testu stosujemy gdy poddajemy analizie tą samą pojedynczą grupę, ale dwukrotnie w czasie. Na przykład gdy chcemy porównać średnie wagi grupy osób przed dietą oraz po diecie, aby sprawdzić czy dieta spowodowała istotne zmiany statystyczne.\n", + "\n", + "Hipoteza zerowa takiego testu będzie brzmiała H0: Średnia waga osób po diecie jest taka sama jak przed dietą. Hipoteza alternatywna z kolei H1: Dieta znacząco wpłynęła na średnią wagę danej grupy." + ] + }, { "cell_type": "code", "execution_count": 160, @@ -154,91 +379,6 @@ " return l1" ] }, - { - "cell_type": "code", - "execution_count": 162, - "metadata": {}, - "outputs": [], - "source": [ - "def df_ind(sample_1, sample_2):\n", - " \"\"\"Funkcja oblicza stopnie swobody dla dwóch próbek niezależnych\"\"\"\n", - " return len(sample_1) + len(sample_2) - 2" - ] - }, - { - "cell_type": "code", - "execution_count": 163, - "metadata": {}, - "outputs": [], - "source": [ - "def df_single(sample_1):\n", - " \"\"\"Funkcja oblicza stopnie swobody dla jednej próbki\"\"\"\n", - " # TODO: I have no clue what to return from here\n", - " return len(sample_1)" - ] - }, - { - "cell_type": "code", - "execution_count": 164, - "metadata": {}, - "outputs": [], - "source": [ - "def calculate_p(t_stat, df):\n", - " \"\"\"Funkcja oblicza wartość *p* na podstawie statystyki testowej i stopni swobody\"\"\"\n", - " return (1.0 - t.cdf(abs(t_stat), df)) * 2.0" - ] - }, - { - "cell_type": "code", - "execution_count": 165, - "metadata": {}, - "outputs": [], - "source": [ - "def calculate_cv(df, alpha=0.05):\n", - " \"\"\"Funkcja oblicza wartość krytyczną (critical value)\"\"\"\n", - " return t.ppf(1.0 - alpha, df)" - ] - }, - { - "cell_type": "code", - "execution_count": 166, - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - }, - "outputs": [], - "source": [ - "def bootstrap_one_sample(sample, population_mean):\n", - " return t_test(\n", - " sample_1=sample,\n", - " df_fn=df_single,\n", - " t_stat_fn=t_stat_single,\n", - " population_mean=population_mean\n", - " )" - ] - }, - { - "cell_type": "code", - "execution_count": 167, - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - }, - "outputs": [], - "source": [ - "def bootstrap_independent(sample_1, sample_2):\n", - " return t_test(\n", - " sample_1=sample_1,\n", - " sample_2=sample_2,\n", - " df_fn=df_ind,\n", - " t_stat_fn=t_stat_ind\n", - " )" - ] - }, { "cell_type": "code", "execution_count": 168, @@ -260,64 +400,19 @@ ] }, { - "cell_type": "code", - "execution_count": 169, + "cell_type": "markdown", "metadata": {}, - "outputs": [], "source": [ - "def get_t_stats(sample_1, sample_2=None, t_stat_fn=t_stat_single, population_mean=None):\n", - " \"\"\"Funkcja oblicza listę statystyk testowych dla każdej próbki bootstrapowej wybranej na podstawie danych sample_1 i sample_2\"\"\"\n", - " t_stat_list = []\n", - "\n", - " # One sample test\n", - " if t_stat_fn==t_stat_single:\n", - " if not population_mean:\n", - " raise Exception(\"population_mean not provided\")\n", - " for bootstrap in generate_bootstraps(sample_1):\n", - " stat = t_stat_fn(bootstrap, population_mean)\n", - " t_stat_list.append(stat)\n", - " return t_stat_list\n", - "\n", - " # Two sample test\n", - " for bootstrap_1, bootstrap_2 in zip(generate_bootstraps(sample_1), generate_bootstraps(sample_2)):\n", - " stat = t_stat_fn(bootstrap_1, bootstrap_2)\n", - " t_stat_list.append(stat)\n", - " return t_stat_list" + "# TODO: Wyciągnąć wagi przed dietą i po oraz poprawić kod aby można było podawać kolumny\n", + "t_stat, df, cv, p, _ = bootstrap_dependent(dataset, dataset)\n", + "pretty_print_full_stats(t_stat, df, cv, p)" ] }, { - "cell_type": "code", - "execution_count": 170, - "metadata": { - "pycharm": { - "name": "#%%\n" - } - }, - "outputs": [], + "cell_type": "markdown", + "metadata": {}, "source": [ - "def t_test(sample_1, sample_2=None, df_fn=df_single, t_stat_fn=t_stat_single, population_mean=None, alpha=0.05):\n", - " \"\"\"\n", - " Funkcja przeprowadza test T-studenta dla dwóch zmiennych.\n", - " liczba kolumn wynosi 1, test jest przeprowadzany dla jednej zmiennej.\n", - " @param df_fn - funkcja obliczająca stopnie swobody\n", - " @param t_stat_fn - funkcja obliczająca statystykę T\n", - " \"\"\"\n", - " t_stat_list = get_t_stats(sample_1, sample_2, t_stat_fn, population_mean=population_mean)\n", - " t_stat_sum = sum(t_stat_list)\n", - "\n", - " data_size = sample_1.shape[0]\n", - "\n", - " t_stat = t_stat_sum / data_size\n", - " # TODO: dolna i górna opcja dają inne wyniki z jakiegoś powodu (???)\n", - " t_stat = mean(t_stat_list)\n", - "\n", - " if sample_2 is None:\n", - " df = df_fn(sample_1)\n", - " else:\n", - " df = df_fn(sample_1, sample_2)\n", - " cv = calculate_cv(df, alpha)\n", - " p = calculate_p(t_stat, df)\n", - " return t_stat, df, cv, p, t_stat_list" + "## Wykresy" ] }, { @@ -342,9 +437,16 @@ " plt.show()" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Testy" + ] + }, { "cell_type": "code", - "execution_count": 172, + "execution_count": 31, "metadata": { "collapsed": false, "pycharm": { @@ -398,21 +500,22 @@ }, { "cell_type": "code", - "execution_count": 173, + "execution_count": 39, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ + "\n", "Statystyki dla jednej próby:\n", - "t: 1.8524997668616348, df: 5, cv: 2.015048372669157, p: 0.12315232406912302\n", + "t: 1.8073147056683616, df: 5, cv: 2.015048372669157, p: 0.13052275003443325\n", "\n", "Statystyki dla dwóch prób zależnych:\n", - "t: 3.166992562129946, df: 5, cv: 2.015048372669157, p: 0.02489883191814224\n", + "t: 3.0790273716290404, df: 5, cv: 2.015048372669157, p: 0.027500015466573435\n", "\n", "Statystyki dla dwóch prób niezależnych:\n", - "t: 3.0429202631473986, df: 8, cv: 1.8595480375228421, p: 0.015992147409949586\n", + "t: 2.8109511013364576, df: 8, cv: 1.8595480375228421, p: 0.02280961069987497\n", "\n" ] } @@ -423,6 +526,8 @@ "def pretty_print_full_stats(t_stat, df, cv, p):\n", " print(f't: {t_stat}, df: {df}, cv: {cv}, p: {p}\\n')\n", "\n", + "print(type(dummy))\n", + "\n", "print('Statystyki dla jednej próby:')\n", "t_stat, df, cv, p, _ = bootstrap_one_sample(dummy, 2)\n", "pretty_print_full_stats(t_stat, df, cv, p)\n", @@ -435,21 +540,6 @@ "t_stat, df, cv, p, _ = bootstrap_independent(dummy2, dummy3)\n", "pretty_print_full_stats(t_stat, df, cv, p)" ] - }, - { - "cell_type": "code", - "execution_count": 174, - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - }, - "outputs": [], - "source": [ - "dataset = pd.read_csv('experiment_data.csv')\n", - "#make_decision(dataset, ['Weight', 'Age'])" - ] } ], "metadata": { @@ -477,4 +567,4 @@ }, "nbformat": 4, "nbformat_minor": 2 -} \ No newline at end of file +}