Business

Analyzing the Lottery Numbers

In my country, there is a weekly lotto game where participants select 6 numbers from a pool of 37 and another number from a pool of 7. Let’s focus on the first part of the game and disregard the selection of one additional number from a pool of 7.

When it comes to lotteries of the form k/N, where k is the number of desired selections (in our case, 6) out of a total pool of N numbers (in our case, 37), a common question is whether each of these numbers has an equal chance of being part of the winning combination.

Let’s investigate this question.

I’ve gathered statistics from their website for 1609 drawings spanning from 2009 to 2023.

Subsequently, I converted the data from the CSV file into an object:

{
 '09/09/2023': [13, 17, 24, 30, 35, 37],
 '07/09/2023': [7, 17, 19, 25, 35, 37],
 '05/09/2023': [2, 3, 5, 9, 36, 37],
 '02/09/2023': [4, 12, 22, 27, 30, 34],
 '29/08/2023': [6, 8, 15, 19, 26, 31],
 '26/08/2023': [6, 7, 14, 21, 25, 34],
 '22/08/2023': [2, 6, 10, 23, 24, 29],
 ...
}

The key in the object corresponds to the date of the drawing, and the associated value is an array of the numbers that emerged as the winning combination for that specific drawing.

Afterward, I created an array containing all the numbers obtained from the drawings:

numbers = np.array(list(lotto.values())).flatten()

[13, 17, 24, 30, 35, 37,  7, 17, 19, 25, 35, 37,  2,  3,  5,  9, 36, ...]

Following that, I calculated the count of occurrences (frequency) for each value within the array:

count = np.bincount(numbers)[1:]

[268, 256, 257, 242, 255, 273, 247, 277, 260, 267, 289, 294, 
 271, 239, 254, 255, 263, 243, 246, 271, 265, 254, 252, 243, 
 291, 271, 258, 264, 275, 258, 251, 244, 263, 256, 267, 251, 264]

These results indicate that the number 1 was drawn 268 times, the number 2 was drawn 256 times, and so on.

It appears that the distribution of numbers in the lottery results is relatively even. To further confirm this, we can conduct a test to validate the evenness of the distribution.

To test the equiprobability of the N individual numbers, you can follow this approach:

  • Calculate the observed frequency (Oi) with which each number i = 1, …, N occurred in the n lottery draws.
  • Compute the expected counts (Ei) for each number using the formula Ei = (nk) / N, where n is the total number of lottery draws, k is the number of numbers selected in each draw (in this case, 6), and N is the total number of possible numbers (in this case, 37).
  • Use the Pearson statistic or chi-squared statistic to compare the observed counts (Oi) with the expected counts (Ei). The formula for the Pearson statistic is often expressed as:

  • Calculate the chi-squared statistic using the observed and expected counts.
  • Perform a statistical test, such as a chi-squared test, to determine if the computed chi-squared value is statistically significant. This will help you assess whether the distribution of numbers is significantly different from what would be expected under equiprobability.

If the calculated chi-squared value is not statistically significant, it suggests that the numbers are reasonably evenly distributed, supporting the hypothesis of equiprobability. However, if the X^2 value is significant, it would indicate a departure from equiprobability.

Let’s create a function to perform the chi-squared test for equiprobability of numbers:

def chi2(data, size, expect, p_value = 0.05):
  pl = size * 1/expect
  df = expect - 1

  x2_crit_1 = stats.chi2.ppf(p_value, df)
  x2_crit_2 = stats.chi2.ppf(1 - p_value, df)

  x2 = 0
  for i in range(expect):
    x2 += ((data[i] - pl) ** 2)/pl

  accepted = x2_crit_1 < x2 < x2_crit_2 if x2_crit_1 < x2_crit_2 else x2_crit_2 < x2 < x2_crit_1

  return x2, accepted

This function returns the tuple consisting of the chi-squared statistic and result that equiprobability accepted with the probability 1 - 2 * p-value , i.e., extreme values of this discrete uniform distribution have low probability.

N = 37

chi2(count, len(numbers), N)

(25.183136523720748, True)

Certainly, you can use the built-in functionality from the SciPy library to perform the chi-squared test for equiprobability:

from scipy import stats

chi2_statistic, p_value = stats.chisquare(count)

(25.18313652372074, 0.9115057832606053)

Let’s explore combinations of these numbers, starting with pairs:

from itertools import combinations

pairs = list(combinations(range(1, N), 2))

Following this step, we construct a 2D matrix that tracks the occurrences of these pairs:

pairs_count = np.zeros([N] * 2, dtype=int)

for pair in pairs:
  for draw in lotto.values():
    if pair[0] in draw and pair[1] in draw:
      pairs_count[pair[0]][pair[1]] += 1

pairs_count = pairs_count[1:, 1:]

This forms a triangular matrix, as it accounts for the fact that pairs (a, b) and (b, a) are equivalent, and we only tally the occurrences of the pairs (a, b).

My function yields:

counts = pairs_count.flatten()
counts = counts[counts > 0]

chi2(counts, sum(counts), len(counts))

(589.2721893491138, True)

and SciPy provides:

chi2_statistic, p_value = stats.chisquare(counts)

(589.2721893491124, 0.8698507423203673)

How about considering triplets:

comb3 = list(combinations(range(1, N), 3))

comb3_count = np.zeros([N] * 3, dtype=int)

for comb in comb3:
  for draw in lotto.values():
    contains = comb[0] in draw and comb[1] in draw and comb[2] in draw
    if contains:
      comb3_count[comb[0]][comb[1]][comb[2]] += 1

comb3_count = comb3_count[1:, 1:, 1:]

counts = comb3_count.flatten()
counts = counts[counts > 0]

chi2(counts, sum(counts), len(counts))

(6457.575829383709, False)

Something has gone awry, possibly due to the matrix’s high sparsity. The chi-squared value falls below the lower critical chi-squared threshold:

6457.575829383709 < 6840.049842653838

However, when using SciPy, the result is:

chi2_statistic, p_value = stats.chisquare(counts)

(6457.575829383886, 0.9999997038479482)

Now, let’s identify the number that has been drawn most frequently:

count.argmax() or list(count).index(max(count))

11

Let’s not jump to conclusions just yet. We can examine how this number has evolved over the years:

year_result = dict()
for year in range(2009, 2024):
  new_dict = {k:v for (k,v) in lotto.items() if str(year) in k}
  year_result[year] = np.bincount(np.array(list(new_dict.values())).flatten())[1:].argmax()

{
 2009: 16,
 2010: 10,
 2011: 11,
 2012: 24,
 2013: 32,
 2014: 34,
 2015: 21,
 2016: 25,
 2017: 5,
 2018: 10,
 2019: 24,
 2020: 11,
 2021: 12,
 2022: 14,
 2023: 11
}

Or, alternatively, we can analyze the cumulative changes over time:

year_result = dict()
arr = []
for year in range(2009, 2024):
  new_dict = {k:v for (k,v) in lotto.items() if str(year) in k}
  arr += list(np.array(list(new_dict.values())).flatten())
  year_result['2009 - ' + str(year) if year > 2009 else str(year)] = np.bincount(arr)[1:].argmax()

{
        '2009': 16,
 '2009 - 2010': 10,
 '2009 - 2011': 11,
 '2009 - 2012': 20,
 '2009 - 2013': 20,
 '2009 - 2014': 20,
 '2009 - 2015': 34,
 '2009 - 2016': 20,
 '2009 - 2017': 10,
 '2009 - 2018': 10,
 '2009 - 2019': 10,
 '2009 - 2020': 10,
 '2009 - 2021': 10,
 '2009 - 2022': 24,
 '2009 - 2023': 11
}

Lastly, we can also investigate whether identical drawings have ever occurred:

lotto_counts = {}
for k, v in lotto.items():
  v_str = str(v)
  if v_str in lotto_counts:
     lotto_counts[v_str] += [k]
  else:
     lotto_counts[v_str] = [k]

result = {k: v for k, v in lotto_counts.items() if len(lotto_counts[k]) > 1}

{
  '[13, 14, 26, 32, 33, 36]': ['16/10/2010', '21/09/2010']
}

It’s amusing to note that these occurrences happened nearly back-to-back.

As we wrap up our journey into the world of lottery data, it’s been a wild ride through numbers and probabilities. We’ve uncovered some interesting tidbits – from pairs and triplets to spotting the most popular numbers.

Lotteries are all about unpredictability, but it’s fun to peek behind the curtain and explore the quirks of these games. Whether you’re a player or just a curious observer, the world of numbers always has a surprise or two up its sleeve.


This article was originally published by Alexthoughts on Hackernoon.

HackerNoon

Recent Posts

GAN, Tec de Monterrey partnership highlights cross-border startup ecosystem building in Latin America amid trade dispute

Despite recent tensions between the United States government and Latin American countries over migration and…

3 days ago

This founder started out with US $5K to his name. Now, he owns a multi-million-dollar global business

Meet Nitin Seth, the Co-Founder and CEO of Screen Magic (SMS Magic), a messaging leader…

3 days ago

Building smarter: AI, the ultimate tool transforming an old-age industry

In this Brains Byte Back, we sit down with Hari Vasudevan, founder and CEO of…

3 days ago

When AI Goes Rogue: 8 Lessons from Implementing LLMs in the Healthcare Industry that Could Save the Future

By Santosh Shevade, Principal Data Consultant at Gramener – A Straive company All pharmaceutical companies…

5 days ago

Digital Public Infrastructure will enable public, private entities to control your access to essential goods, services & mobility

Digital Public Infrastructure is a top-down agenda coming from unelected globalists, bureaucrats, and their partners…

2 weeks ago

Open Source Claims to Be a Meritocracy—So Why Are Companies Buying Their Way In?

Imagine that you are a maintainer of a widely used open source project relied upon…

2 weeks ago