How to create a Secret Santa algorithm with Python and Numpy random numbers

Damien BENESCHI
7 min readDec 5, 2017

--

The principle of the Secret Santa is to organize a draw with all the participants of the game (your family members, your colleagues at work, your group of friends) and pick-up who will offer a gift to whom for Christmas.

This year, I decided to do the draw for my family gathering using a little Python script.
Why the Numpy library ? Because this library features the array objects, similar to lists but way easier to manipulate especially if there are many participants, and features also random numbers thanks to the function numpy.random.

Organization of the draw (specifications of the script)

The draw is organized as follow:
- The argument of the game is the list of the names of the participants ;
- Each participant has an integer number attributed (the index in the list of participants) ;
- The random generation picks-up a participant from a list constituted with only the participants that have not been picked-up yet ;
- The picked-up participant receives a gift from the previous picked-up person and offers one to the next picked-up person ;
- Everybody offers and receives a gift.

In addition, we want the script to copy the results of the draw in the clipboard, so they can be easily shared with the participants then.

The participants list

First step is to have the list of the participants, formatted as a Python list object:

participants = [‘Damien’, ‘Maxime’, ‘Guy’, ‘Christine’, ‘Clément’, ‘Gaëlle’, ‘Denis’, 
‘Katia’, ‘Maelys’, ‘Josian’, ‘Lucas’, ‘Isabelle’, ‘Pascal’, ‘Julien’]

This list can be done either manually, but in the situation with lots of participants it can be exhausting (and boring), either automatically.

If the list is already available in a Google spreadsheet (generated by a Google form for example), then the libraries pandas and numpy will be useful.

Google spreadsheet with the list of participants

First step is to export the list of participant as a .csv file (“comma separated values”) that can be read by the pandas library. To do so, go in “File/Download as/ (.csv file)” and save it in the same folder than the Python script.

If the list is already in an Excel file in one column like in the example, just save it as a .csv file.

Next step is to install the required libraries in Python, if not already done, using pip in the command invite:

$ pip install numpy
$ pip install pandas

The script to generate the participants list object from the .csv file is the following one:

import pandas
import numpy as np
def get_list(file):
participants = pandas.read_csv(file, header=None,
names=np.array(['Names']), encoding='utf-8')
participants = list(participants['Names'])
return (participants)

The libraries pandas and numpy are imported in order to be able to use their methods. The script is defined as a function that takes as an argument the name of the .csv file that contains the list of participants, and that is saved in the script folder.

The spreadsheet is read by the pandas functionpandas.read_csv(), that takes file as a mandatory argument and optional other arguments (full list available in the documentation here).

Here, the arguments for the function are:

  • file: the name of the .csv file as a string object (surrounded by ‘’) ;
  • header=None: specifies here that the column with the names has no header, otherwise the read_csv() method takes the first item of the column as the label of the values (on the example, it would take ‘Damien’ as the label, not as a value) ;
  • names=np.array(['Names']): attributes ‘Names’ as the label of the column, that must be a numpy.array object here, of one element in our case ;
  • encoding='utf-8': specifies the type of encoding of the data.

The read_csv() method attributes to the variable participants a object type Pandas.DataFrame, similar to a spreadsheet in the structure, that has here one single column labeled ‘Names’ and containing the names of the participants.

To get a list object from this DataFrame object, the values are selected using the DataFrame['col_label'] method that returns a pandas.Series object, and then converted into a list object using the list() method:

participants = list(participants[‘Names’])

The function returns from the spreadsheet the list of the participants that suits the format requirements for the Secret Santa script.

The Secret Santa script

Now that the list of participants is ready to use, let’s develop the draw.

Initialization

The Secret Santa is defined as a function that takes the list of names as an argument. Since the random function generates integers, the first step is to pair each participant name with an integer. The most simple way to do it is to pair a name with its index in the participants list, that goes from 0 to N-1 where N is the number of participants, as Python interprets the list objects index.

As the script will use numpy array objects, the first step is to create an array object of all the participants integers in order to constitute the list of the initial values that can be picked-up by the draw, from 0 to N-1.

The list of the integers for each participant is created using the list comprehension and thelist.index() methods. It is then converted into a numpy array object:

to_pickup = np.array([participants.index(name) for name in participants])

Next step is to initialize the draw by picking the first name.

The numpy random methods use a “seed” to generate pseudo-random numbers, based on a mathematical function defined in the method. The same numbers are generated with a given seed, that is why it is called “pseudo-random” numbers.

The seed is given with the function np.random.seed(integer) that takes an integer of your choice (year of birth for example).

The random number is then generated and associated to the variable r by r = np.random.choice(to_pickup) that takes as an argument the list of possible values to pick-up. This is crucial here as we can change this list by removing the picked-up participants after each draw. The r variable is an integer corresponding to a participant, that is used afterwards to get the paired participant name using the list method participants[r].
Another numpy array object called pickedup = np.array([]) is created and will be appended with the picked-up values during the draw, and will be useful to display then the name of the corresponding participant to fill a string with the result of each draw.

The initialization script is the following one:

pickedup = np.array([])
results = ""
np.random.seed(3690)
r = np.random.choice(to_pickup)
print("picked-up first: " + participants[r] + '\n')
to_pickup = np.delete(to_pickup, np.where(to_pickup == r))
pickedup = np.append(pickedup, r)

The script prints-out the first name that has been picked-up, the picked-up value is removed from the possible values set and the list of picked-up participants is appended.

To remove a value in a numpy array object, the functions numpy.delete() and numpy.where() are combined. The first function takes as arguments the array to process and the index of the value to remove. To get the index, the second method is used to get the index of the first value that satisfies the condition, here to_pickup == r.

The loop for draws

The loop for draws is a for loop that will proceed N-1 draws for a set of N participants after the first participants has been selected.

The forloop is based on the elements of the to_pickup numpy array, that has now N-1 values after the initial draw.

Then for each draw, an integer from the remaining participants numpy arrayto_pickup is selected randomly, the value is taken out from the to_pickupset, the value is added to the picked_up array, and the result of the round is added to the string variable results in order to finally display the global results of the draws and copy them to the clipboard.

The script for the loop is the following one:

for person in to_pickup:
r = np.random.choice(to_pickup)
pickedup = np.append(pickedup, r)
to_pickup = np.delete(to_pickup, np.where(to_pickup == r))
results = results + 'And ' + participants[int(pickedup.item(-1))] + ' offers to ' + participants[int(pickedup.item(0))] + '\n'

The resultvariable is composed of lines (ended by a line break '\n') that take in the participant list the previous picked-up participant name and the current one.

The method pickedup.item(-1) applied to the “pickedup” array returns the last value (index -1). But here, the type of the number returned by this method is a float, not an int, and can’t be used as an index. The the trick is to turn it into an integer using the int() method prior to used it to get the corresponding name in the participants list.

Ending of the draw

Once all the participants have been picked-up, the last one offers a gift to the very first one:

results = results + 'And ' + participants[int(pickedup.item(-1))] + ' offers to ' + participants[int(pickedup.item(0))] + '\n'

Copy the results to the clipboard

The last part of the script aims to copy the results (stored in the string object results with the list of offers/receives) to the clipboard in order to share it easily.

This is possible thanks to library pyperclip (that must be imported too).

The useful functions of this library are pyperclip.copy()and pyperclip.paste()in order to read the variable resultsand send the content to the clipboard of the computer:

pyperclip.copy(results)
clipboard = pyperclip.paste()

Change the randomly generated list

The list is generated by a pseudo-random method, which means that if you do the simulation again, you will have similar results.

To change the results, the random numbers generation must bechanged by giving the seedvariable another value of your choice.

The entire python script is available on GitHubGist here.

--

--

Damien BENESCHI

Energy Specialist & Data Scientist | Interested in Sustainable Development, Innovation and Entrepreneurship