Pile ou face

Tout d'abord, quelques initialisations pour python.

In [1]:
%matplotlib inline
from pilouface import PileOuFace, color_sequence, get_longest_sequence, \
                      str2ndarray, print_square, plot_echantillon_hist, \
                      generate_animation
import numpy as np
from matplotlib import rcParams
import matplotlib.pyplot as plt
from matplotlib.animation import PillowWriter
import pandas as pd
rcParams['figure.figsize'] = (8., 6.)  # Enlarge figure

Une suite aléatoire de taille 100

On s'intéresse à la suite aléatoire de taille $n = 100$ à valeur pile $(P)$ ou face $(F)$. Un tirage donne par exemple la suite suivante :

In [2]:
sample = np.random.choice(a=['P', 'F'], size=100)
color_sequence(sample)
Out[2]:
FFFPFFPFPPFPPPPFPFPFFFPFPPFFFFPFPFFFPPFFPPFFFPFFPPFFPFPFPPFFPFPPPFPPPPFPFPFPFPFFFPFPFFPFFPFFFFPFFPPF [100]

On veut étudier l'occurrence de séquences répétées de pile ou de face.

Dans la suite précédente, on met en gras les plus longues séquences consécutives de pile et de face.

In [3]:
color_sequence(sample, strong=True)
Out[3]:
FFFPFFPFPPFPPPPFPFPFFFPFPPFFFFPFPFFFPPFFPPFFFPFFPPFFPFPFPPFFPFPPPFPPPPFPFPFPFPFFFPFPFFPFFPFFFFPFFPPF [100]

On remarque la plus longue séquence de pile :

In [4]:
long_pile, _ = get_longest_sequence(str2ndarray(sample), select='P')
color_sequence('P'*long_pile, strong=True)
Out[4]:
PPPP [4]

et la plus longue séquence de face :

In [5]:
long_face, _ = get_longest_sequence(str2ndarray(sample), select='F')
color_sequence('F'*long_face, strong=True)
Out[5]:
FFFF [4]

Fréquence d'apparition des plus longues séquences

Pour observer la fréquence d'apparition de ces séquences :

On génère 100000 échantillons de taille 100

In [6]:
pof = PileOuFace(nsample=100000)

Pour chaque échantillon, on calcule la longueur de la plus grande séquence consécutive de pile

In [7]:
pof.compute_sequences()
  2% (2412 of 100000) |                  | Elapsed Time: 0:00:00 ETA:   0:00:08
Computing 100000 samples
100% (100000 of 100000) |################| Elapsed Time: 0:00:08 Time:  0:00:08

Pour chaque longueur de séquence maximale, on calcule sa fréquence d'apparition

In [8]:
pof.get_histogram1D()
Out[8]:
Pile Face
0 0.00000 0.000000
1 0.00000 0.000000
2 0.00029 0.000270
3 0.02715 0.027290
4 0.15918 0.161752
5 0.26506 0.264743
6 0.23049 0.228072
7 0.14740 0.147851
8 0.08385 0.081751
9 0.04242 0.043570
10 0.02181 0.022790
11 0.01116 0.011050
12 0.00554 0.005670
13 0.00298 0.002510
14 0.00132 0.001280
15 0.00063 0.000690
16 0.00030 0.000390
17 0.00021 0.000170
18 0.00013 0.000100
19 0.00006 0.000020
20 0.00001 0.000010
21 0.00001 0.000000
22 0.00000 0.000000
23 0.00000 0.000020

On trace ces fréquences sous la forme d'un histogramme

In [9]:
fig = pof.plot1D()
fig.savefig("hist_pile_face.png")

On s'intéresse maintenant aux couples des plus longues séquences. Pour chaque couple, on calcule sa fréquence d'apparition et on la représente sur une figure à deux dimensions.

In [10]:
fig = pof.plot2D()
fig.savefig("couples.png")

Carrés $3 \times 3$

Pour simplifier les formulations, nous remplaçons les piles et les faces par des 0 et des 1.

On arrange les éléments d'un tirage aléatoire de taille $n = 100$, dans un carré de taille $10 \times 10$. Dans ce carré, on compte le nombre de sous-carrés de taille $3 \times 3$ dont la somme des éléments vaut :

  • soit 0 (tous les éléments valent 0)
  • soit 1 (tous valent 0 sauf 1)
  • soit 8 (tous valent 1 sauf 1)
  • soit 9 (tous valent 1)

Exemple avec :

In [11]:
print_square()

Un carré 3 x 3 de somme 1

0010011100
1010100111
0001000101
0101011100
1111001011
1001000111
1000010101
0100000000
1100011011
1111010100

On initialise un tirage de 100000 échantillons.

In [12]:
pof = PileOuFace(100000)

On calcule l'histogramme de la somme des carrés.

In [13]:
pof.compute_3x3_squares()
N/A% (0 of 100000) |                     | Elapsed Time: 0:00:00 ETA:  --:--:--
Computing 100000 samples for 3x3 squares
100% (100000 of 100000) |################| Elapsed Time: 0:00:07 Time:  0:00:07

On trace l'histogramme.

In [14]:
fig = pof.plot_3x3_squares()
fig.savefig("hist_3x3.png")
In [15]:
pof.histo_nsquares
Out[15]:
Probabilité 3x3
0 0.205024
1 0.210004
2 0.180414
3 0.134473
4 0.095022
5 0.064871
6 0.042701
7 0.026721
8 0.016750
9 0.009920
10 0.005570
11 0.003630
12 0.002300
13 0.001110
14 0.000560
15 0.000370
16 0.000330
17 0.000080
18 0.000040
19 0.000050
20 0.000050
21 0.000010
22 0.000000

Moyenne

In [16]:
pof.mean
Out[16]:
2.50621

Ecart-type

In [17]:
pof.std
Out[17]:
2.3678009704998435

Analyse d'échantillons de tirages "pile ou face" sous forme de tableaux $10 \times 10$

Les données sont contenus dans le fichier cadres.csv, que l'on lit avec la bibliothèques Pandas :

  • chaque ligne représente la réalisation d'un tableau $10 \times 10$,
  • chaque colonne représente un échantillon de 10 personnes appartenant à la même catégorie de population,
  • l'entier désigne le nombre d'occurrences d'un carré $3 \times 3$ dans le tirage.
In [18]:
df = pd.read_csv("cadres.csv")
df = df[:-2].astype('int32')  # On convertit en entiers courts
df = df.drop('N°', axis=1)  # On enlève la première colonne
df
Out[18]:
ECH A ECH B ECH C ECH D
0 1 1 3 10
1 0 4 0 3
2 0 7 1 11
3 0 0 2 2
4 5 9 0 2
5 3 1 2 7
6 6 4 0 5
7 1 0 1 3
8 2 1 0 6
9 7 3 2 6

Pour chaque échantillon, on trace l'histogramme de la fréquence des carrés $3 \times 3$, en superposant l'histogramme calculé sur 100000 échantillons aléatoires.

Les figures générées sont sauvegardées dans les fichiers hist_*.png.

In [19]:
echantillons = 'A', 'B', 'C', 'D'

for echantillon in echantillons:
    s = df[f'ECH {echantillon}']
    s.name = f"Echantillon {echantillon}"
    fig = plot_echantillon_hist(s, pof, xmax=df.max().max())
    fig.savefig(f'hist_{echantillon}.png')

Afin de vérifier que la taille de nos échantillons $(n = 10)$ est suffisante pour que le classement soit significatif, on compare les 100000 tirages aléatoires à un échantillon de 10 tirages aléatoires.

In [20]:
pof10 = PileOuFace(nsample=10)
s10 = pof10.compute_3x3_squares(ret_df=True, verbose=False)
fig = plot_echantillon_hist(s10, pof, xmax=df.max().max())
fig.savefig('hist_10_tirages.png')

Pour mieux se rendre compte, on fait défiler en boucle sur 10 séries de 10 tirages.

In [21]:
anim = generate_animation(nsample=10, nframe=10, large_pof=pof, xmax=df.max().max())
anim.save('animation.gif', PillowWriter(fps=2))
plt.close(anim._fig)

animation

Toujours dans l'objectif de trier les échantillons, on calcule la moyenne et l'écart-type du nombre de carrés $3 \times 3$.

In [22]:
stat = pd.DataFrame(index=echantillons, columns=['mean', 'std'])
for echantillon in echantillons:
    s = df[f'ECH {echantillon}']
    stat['mean'][echantillon] = s.mean()
    stat['std'][echantillon] = s.std()

# On ajoute l'échantillon de taille 100 000
stat = stat.append(pd.Series({'mean': pof.mean, 'std': pof.std}, name=pof.name))

# On ajoute les échantillons de taille 10
for echantillon in range(10):
    pof10 = PileOuFace(nsample=10)
    pof10.compute_3x3_squares(verbose=False)
    s10 = pd.Series({'mean': pof10.mean, 'std': pof10.std}, name=f"10-tirs ({echantillon + 1})")
    stat = stat.append(s10)

stat
Out[22]:
mean std
A 2.5 2.63523
B 3 3.05505
C 1.1 1.1005
D 5.5 3.17105
100000 tirages aléatoires 2.50621 2.3678
10-tirs (1) 2.4 1.49666
10-tirs (2) 2.1 1.81384
10-tirs (3) 2.5 2.41868
10-tirs (4) 2.7 2.45153
10-tirs (5) 3.3 3.40735
10-tirs (6) 5.3 3.19531
10-tirs (7) 1.7 1.55242
10-tirs (8) 2.1 1.3
10-tirs (9) 1.8 1.66132
10-tirs (10) 2.6 2.45764

On représente ces valeurs sous forme d'un nuage de points

In [23]:
fig = plt.figure()
ax = fig.add_subplot(111)
for index, row in stat.iterrows():
    if index in echantillons:
        marker = {'markersize': 10, 'c': 'r'}
        text = {'fontsize': 14, 'color': 'r'}
    elif index == '100000 tirages aléatoires':
        marker = {'markersize': 10, 'c': 'b'}
        text = {'fontsize': 10, 'color': 'b'}
    else:
        marker = {'markersize': 6}
        text = {'fontsize': 10}
    ax.plot(row['mean'], row['std'], 'o', **marker)
    ax.annotate(index, (row['mean'] + 0.1, row['std']), **text)

ax.set_xlabel('Moyenne')
ax.set_ylabel('Ecart-type')
ax.set_title('Nuage de points des échantillons', fontsize=14);
fig.savefig('scatterplot.png')