"# Generowanie losowych obiektów w Pythonie. Znane rozkłady prawdopodobieństwa w Pythonie. "
]
},
{
"cell_type": "markdown",
"id": "bdfc62",
"metadata": {
"collapsed": false
},
"source": [
"## Biblioteka NumPy\n",
"\n",
"**NumPy** (Numerical Python) jest biblioteką dla języka Python, której głównym zadaniem jest umożliwienie pracy na dużych, wielowymiarowych tabelach i macierzach. Zacznijmy od zaimportowania biblioteki za pomocą poniższego polecenia."
]
},
{
"cell_type": "code",
"execution_count": 13,
"id": "1498f3",
"metadata": {
"collapsed": false
},
"outputs": [
],
"source": [
"import numpy as np"
]
},
{
"cell_type": "markdown",
"id": "ac566b",
"metadata": {
"collapsed": false
},
"source": [
"Ponieważ głównym obiektem biblioteki NumPy są macierze, czyli obiekty klasy `ndarray` (N dimensional array), zobaczmy na początek w jaki sposób z nimi pracować. Obiekty tego typu mają zazwyczaj ustalony rozmiar, który wyznaczony jest przez kształt macierzy, czyli `shape`. Np. `shape = (2,2,4)` dotyczy macierzy trójwymiarowej o wymiarach $2\\times 2\\times 4$ i $2\\cdot 2\\cdot 4 = 16$ polach."
]
},
{
"cell_type": "code",
"execution_count": 37,
"id": "60fe48",
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Macierz A\n",
" [[1 2 3]\n",
" [2 3 4]] \n",
"\n",
"Macierz jedynek\n",
" [[1. 1. 1. 1.]\n",
" [1. 1. 1. 1.]\n",
" [1. 1. 1. 1.]] \n",
"\n",
"Macierz zer\n",
" [[0. 0.]\n",
" [0. 0.]]\n"
]
}
],
"source": [
"# tworzymy macierz typu ndarray\n",
"A = np.array([[1, 2, 3], [2, 3, 4]])\n",
"print(\"Macierz A\\n\", A, \"\\n\")\n",
"\n",
"# tworzymy macierz o wymiarach 3x4 składającą się z samych jedynek\n",
"# tworzymy macierz o w ymiarach 2x2 składającą się z samcyh zer\n",
"A_zeros = np.zeros((2, 2))\n",
"print(\"Macierz zer\\n\", A_zeros)"
]
},
{
"cell_type": "markdown",
"id": "7ea41f",
"metadata": {
"collapsed": false
},
"source": [
"Na macierzach możemy wykonywać standardowe działania takie jak mnożenie, co przyda nam się na późniejszych zajęciach przy okazji pracy z łańcuchami Markowa."
]
},
{
"cell_type": "code",
"execution_count": 54,
"id": "2b527e",
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Macierz B\n",
" [[1 2]\n",
" [1 4]\n",
" [5 0]\n",
" [5 1]] \n",
"\n",
"Wektor v\n",
" [1 2] \n",
"\n",
"Wynik mnożenia B przez v\n",
" [5 9 5 7]\n"
]
}
],
"source": [
"# Tworzymy macierz B o wymiarach 4x2 oraz wektor v\n",
"Podstawowym narzędziem, z którego będziemy korzystać jest generator liczb (i innych obiektów) losowych (a dokładniej rzecz ujmując pseudolosowych). Aby wygenerować losową liczbę całkowitą możemy posłużyć się funkcją `randint(n)` (zwracającą losową liczbę całkowitą z przedziału `[0, n)` zgodnie z **rozkładem jednostajnym**, czyli takim, gdzie każda liczba z podanego przedziału ma szanse pojawić się z równym prawdopodobieństwem)."
"# generujemy 10 liczb losowych z przedziału [0,100]\n",
"for _ in range(10):\n",
" x = np.random.randint(100)\n",
" print(x)"
]
},
{
"cell_type": "code",
"execution_count": 20,
"id": "915a84",
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[[60 12 38 99 87 40 22 92 79 66]\n",
" [27 30 90 72 35 52 67 80 51 26]]\n"
]
}
],
"source": [
"# generujemy tablicę typu ndarray o wymiarach 2 x 10 wypełnioną losowymi liczbami całkowitymi z przedziału [0, 100]\n",
"\n",
"x = np.random.randint(100, size=(2, 10))\n",
"print(x)"
]
},
{
"cell_type": "markdown",
"id": "050663",
"metadata": {
"collapsed": false
},
"source": [
"NumPy daje nam również możliwość losowania dowolnych obiektów. Służy do tego funkcja `choice`, która przyjmuje następujące argumenty:\n",
" - `a` - może być jednowymiarową macierzą typu `ndarray`, wówczas losowane są elementy z tej macierzy, `a` może być też liczbą naturalną, wówczas losowane są liczby naturalne z przedziału `[0, a]`,\n",
" - `size` - determinuje ile obiektów jest losowanych, może być to liczba lub wektor liczb, domyślnie `size` przyjmuje wartość `None`co oznacza, że losowany jest jeden obiekt,\n",
" - `replace` - czy losujemy ze zwracaniem, czy bez zwracania, domyślnie przyjmuje wartość `True` co oznacza, że dany obiekt może zostać wylosowany wielokrotnie,\n",
" - `p` - wektor prawdopodobieństw, jeśli chcemy by losowanie odbywało się nie w sposób jednostajny, tylko ze z góry zadanym rozkładem.\n",
" \n",
" **Przykład 1**\n",
" \n",
" Chcemy wylosować:\n",
" - jedną kartę\n",
" - $5$ kart otrzymanych przez pojedynczego gracza\n",
" - rozdanie po $5$ kart dla czterech graczy\n",
" \n",
" ze standardowej talii $52$ kart. W tym celu najpierw definiujemy talię, a następnie stosujemy funkcję `choice` z odpowiednimi parametrami."
]
},
{
"cell_type": "code",
"execution_count": 25,
"id": "63563b",
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Losowa karta: 10 karo \n",
"\n",
"Rozdanie 5 losowych kart dla jednego gracza: ['7 trefl' '7 kier' '5 karo' 'As pik' '2 pik'] \n",
"\n",
"Rozdanie po 5 losowych kart dla czterech graczy: \n",
"print(\"Rozdanie po 5 losowych kart dla czterech graczy: \\n\", z)"
]
},
{
"cell_type": "markdown",
"id": "0ca5a9",
"metadata": {
"collapsed": false
},
"source": [
"**Przykład 2**\n",
"\n",
"Chcemy wylosować $10$ razy ze zwracaniem jedną kulę z urny zawierającej $3$ białe, $4$ czerwone i $5$ zielonych kul. Możemy tutaj zastosować dwa podejścia:\n",
" - losujemy w sposób jednostajny jeden obiekt z tablicy zawierającej $3+4+5=12$ elementów, które odpowiadają naszym kulom,\n",
" - losujemy jeden z kolorów zgodnie z rozkładem, który odpowiada częstotliwościom występowania każdego z kolorów, czyli przyjmując wektor prawdopodobieństw $p = (3/12, 4/12, 5/12)$."
"y = np.random.choice(kolory, size=10, p=prawdop)\n",
"print(y)"
]
},
{
"cell_type": "markdown",
"id": "01e68a",
"metadata": {
"collapsed": false
},
"source": [
"Przeprowadźmy teraz $1000$-krotne losowanie ze zwracaniem kuli z naszej urny i porównajmy częstotliwość wyboru każdego z kolorów z częstotliwością występowania tego koloru w urnie. Proszę zauważyć, że\n",
"Jeśli nasz generator faktycznie zwraca obiekty w sposób losowy, spodziewamy się, że uzyskane częstotliwości występowania kolorów podczas losowania będą odpowiadały częstotliwościom występowania każdego z kolorów w urnie."
"**SciPy** jest bogatą biblioteką, która umożliwia wykonywanie skomplikowanych obliczeń tj. całkowanie czy optymalizacja. My natomiast wykorzystamy funkcje związane ze znanymi rozkładami prawdopodobieństwa dostępne dzięki pakietowi `stats`. Będą nas interesowały już poznane rozkłady dyskretne:\n",
" - rozkład jednostajny (na skończonym zbiorze liczb): `randint`\n",
" \n",
" a także rozkłady ciągłe:\n",
" - rozkład jednostajny: `uniform`\n",
" - rozkład wykładniczy: `expon`\n",
" - rozkład standardowy normalny: `norm`.\n",
" \n",
" Zacznijmy od zaimportowania odpowiedniego pakietu: `stats`."
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "8a7728",
"metadata": {
"collapsed": false
},
"outputs": [
],
"source": [
"from scipy import stats"
]
},
{
"cell_type": "markdown",
"id": "a519aa",
"metadata": {
"collapsed": false
},
"source": [
"Omówimy teraz pokrótce niektóre z wyżej wymienionych rozkładów. Przy czym należy pamiętać, że dla każdego z rozkładów zdefiniowane są podobne metody, zatem nie będziemy ich za każdym razem powtarzać. Metody, z których będziemy najcześciej korzystać przy okazji **rozkładów dyskretnych** to:\n",
" - `pmf` czyli **funkcja masy prawdopodobieństwa** (*ang. probability mass function*),\n",
" - `cdf` czyli **dystrybuanta** (*ang. cumulative distribution function*),\n",
" - `mean` czyli **wartość oczekiwana** (*ang. mean value* or *expectation*),\n",
" - `var` czyli **wariancja** (*ang. variance*),\n",
" - `std` czyli **odchylenie standardowe** (*ang. standard deviation*) będące po prostu pierwiastkiem z wariancji,\n",
" - `rvs` czyli funkcja zwracająca **losową próbkę** (*ang. random sample*) zgodnie z rozkładem zadanej zmiennej losowej \n",
" \n",
" Z kolei do pracy z **rozkładami ciągłymi**, poza wyżej wymienionymi metodami (oprócz `pmf`, która nie ma sensu dla rozkładów ciągłych) przyda nam się dodatkowo metoda:\n",
" - `pdf` czyli **gęstość rozkładu** (*ang. probability density function*).\n",
"\n",
"### Rozkład dwumianowy stats.binom\n",
"\n",
"Przypomnijmy, że rozkład dwumianowy $Bin(n,p)$ z parametrami $n$ i $p$ dotyczy $n$ niezależnych prób Bernoulliego z prawdopodobieństwem sukcesu w pojedynczej próbie wynoszącym $p \\in (0,1)$, a zmienna losowa o tym rozkładzie zwraca liczbę uzyskanych sukcesów. Do wygenerowania takiej zmiennej losowej służy funkcja `stats.binom` przyjmująca dwa argumenty $n$ i $p$.\n",
" \n",
" **Przykład 3**\n",
" \n",
" Wyznaczymy funkcję masy prawdopodobieństwa, wartość oczekiwaną oraz wariancję zmiennej losowej $X$, która zlicza liczbę czwórek wyrzuconych w $12$ rzutach standardową kostką. Wiemy zatem, że $$X \\sim Bin(12, 1/6).$$"
]
},
{
"cell_type": "code",
"execution_count": 9,
"id": "8ebffc",
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Funkcja masy prawdopodobieństwa zmiennej losowej X\n",
"P(X = 0 ) = 0.11215665478461513\n",
"P(X = 1 ) = 0.2691759714830762\n",
"P(X = 2 ) = 0.29609356863138386\n",
"P(X = 3 ) = 0.19739571242092233\n",
"P(X = 4 ) = 0.08882807058941508\n",
"P(X = 5 ) = 0.028424982588612827\n",
"P(X = 6 ) = 0.006632495937342999\n",
"P(X = 7 ) = 0.001136999303544513\n",
"P(X = 8 ) = 0.00014212491294306426\n",
"P(X = 9 ) = 1.263332559493902e-05\n",
"P(X = 10 ) = 7.579995356963424e-07\n",
"P(X = 11 ) = 2.7563619479866997e-08\n",
"P(X = 12 ) = 4.593936579977831e-10\n",
"Wartość oczekiwana zmiennej losowej X = 2.0\n",
"Wariancja zmiennej losowej X = 1.6666666666666667\n"
]
}
],
"source": [
"# definiujemy zmienną losową X o dwumianowym rozkładzie prawdopodobieństwa z parametrami n=12 i p=1/6\n",
"X = stats.binom(12, 1/6)\n",
"print(\"Funkcja masy prawdopodobieństwa zmiennej losowej X\")\n",
"for k in range(13):\n",
" print(\"P(X =\", k,\") = \", X.pmf(k))\n",
"print(\"Wartość oczekiwana zmiennej losowej X = \", X.mean())\n",
"print(\"Wariancja zmiennej losowej X = \", X.var())"
]
},
{
"cell_type": "markdown",
"id": "66310d",
"metadata": {
"collapsed": false
},
"source": [
"### Rozkład geometryczny\n",
"\n",
"Rozkład geometryczny $Ge(p)$ z parametrem $p\\in(0,1)$ dotyczy eksperymentu, w którym powtarzamy niezależne próby Bernoulliego z prawdopodobieństwem sukcesu w pojedynczej próbie równym $p$, do momentu uzyskania pierwszego sukcesu. Zmienna losowa o tym rozkładzie zwraca liczbę takich prób i możemy ją wygenerować za pomocą polecenia `stats.geom`. \n",
"\n",
"**Przykład 4**\n",
"\n",
"Przedstawimy na wykresie funkcję masy prawdopodobieństwa zmiennej losowej o rozkładzie geometrycznym z parametrem $p=1/8=0{.}125$. Do rysowania wykresów korzystać będziemy z biblioteki **matplotlib**."
"# definiujemy zmienną losową Y o rozkładzie geometrycznym z parametrem p=0.125\n",
"Y = stats.geom(0.125)\n",
"\n",
"# wartości do wykresu\n",
"x = np.arange(1, 26)\n",
"pmf_values = Y.pmf(x)\n",
"\n",
"# tworzenie wykresu\n",
"plt.bar(x, pmf_values)\n",
"plt.title('Funkcja masy prawdopodobieństwa zmiennej losowej Y')\n",
"plt.xlabel('k')\n",
"plt.ylabel('P(Y=k)')\n",
"plt.xticks(x)\n",
"plt.grid()\n",
"plt.show()"
]
},
{
"cell_type": "markdown",
"id": "fd54b0",
"metadata": {
"collapsed": false
},
"source": [
"### Rozkład Poissona\n",
"\n",
"Zmienna losowa $X$ ma rozkład Poissona z parametrem $\\lambda\\in (0,+\\infty)$, jeśli jej funkcja masy prawdopodobieństwa dana jest wzorem:\n",
"\n",
"$$\\mathbb{P}(X=k)=\\frac{\\lambda^k}{k!}e^{-\\lambda} \\quad \\text{ dla } \\quad k=0,1,2,\\ldots$$\n",
"\n",
"Rozkładu tego używamy najczęściej aby modelować zdarzenia ,,rzadkie'' np. liczbę wypadków drogowych, liczbę pożarów budynku itp. Wówczas parametr $\\lambda$ odnosi się do średniej wartości tej zmiennej losowej. Zmienną losową o tym rozkładzie wygenerujemy za pomocą polecenia `stats.poisson`.\n",
"\n",
"**Przykład 5**\n",
"\n",
"Porównajmy ze sobą rozkład Poissona i rozkład dwumianowy. W tym celu rozważymy zmienną losową $X \\sim Bin(n, \\frac{\\lambda}{n})$ oraz zmienną losową $Y \\sim Po(\\lambda)$, tak żeby\n",
"Wygenerujemy funkcje masy prawdopodobieństwa dla obu tych rozkładów i dla porównania przedstawimy je na jednym wykresie. Jak się okazuje, dostajemy bardzo podobny rozkład prawdopodobieństwa, co nie jest przypadkiem, ponieważ rozkład dwumianowy $Bin(n,p)$ można dobrze przybliżać rozkładem Poissona $Po(\\lambda)$ przy założeniu, że $p = \\frac{\\lambda}n$. Jako ćwiczenie proszę pozmieniać parametry $\\lambda$, $n$ i $p=\\frac{\\lambda}{n}$."
"gdzie parametry $\\mu$ i $\\sigma$ oznaczają odpowiednio wartość oczekiwaną i odchylenie standardowe. Specjalnym przypadkiem jest **standardowy rozkład normalny** $\\mathcal{N}(0, 1)$, czyli rozkład normalny o parametrach $\\mu=0$ i $\\sigma=1$, dla którego gęstość dana jest wzorem\n",
"W przypadku rozkładów ciągłych prawdopodobieństwo wyznacza się poprzez obliczenie odpowiedniej całki oznaczonej. Np. jeśli $X\\sim\\mathcal{N}(0,1)$, to dla dowolnego zbioru (borelowskiego) $A$ zachodzi\n",
"Obliczanie takich całek w wielu sytuacjach jest bardzo skomplikowane (rozkład normalny jest tutaj dobrym przykładem) i wymaga zastosowania odpowiednich metod numerycznych.\n",
"\n",
"Rozkład normalny jest jednym z najważniejszych rozkładów prawdopodobieństwa i ma ogromne zastosowanie przede wszystkim w statystyce. Zobaczmy na początek jak wygląda gęstość standardowego rozkładu normalnego. Ponieważ rozkład normalny z dowolnymi parametrami da się uzyskać ze standardowego rozkładu normalnego przez odpowiednie przeskalowanie, wykres ten będzie miał zawsze taki sam kształt a reprezentująca go krzywa zwana jest **krzywą Gaussa** lub **krzywą dzwonową** (ze względu na swój kształt)."
"plt.title('Funkcja gęstości prawdopodobieństwa dla rozkładu normalnego standardowego')\n",
"plt.xlabel('x')\n",
"plt.ylabel('f(x)')\n",
"plt.grid()\n",
"plt.legend()\n",
"plt.show()"
]
},
{
"cell_type": "markdown",
"id": "0436fe",
"metadata": {
"collapsed": false
},
"source": [
"**Przykład 6**\n",
"\n",
"Tym razem porównamy rozkład normalny z rozkładem Poissona. Ponieważ rozkład normalny jest rozkładem ciągłym, a rozkład Poissona jest rozkładem dyskretnym, porównamy dystrybuanty obu rozkładów na przykładzie zmiennych losowych $Z \\sim \\mathcal{N}(\\mu, \\sigma)$ i $Y \\sim Po(\\lambda)$. Parametry dobierzemy w taki sposób, aby\n",
"Okazuje się, że jeśli tylko $\\lambda$ jest duża i zachodzą powyższe równości, to rozkład Poissona dobrze przybliża rozkład normalny. Zatem przyjmijmy, że $Z \\sim \\mathcal{N}(100, 10)$ i $Y \\sim Po(100)$, a następnie narysujmy wykresy obydwu dystrybuant."