I would suggest following this course with the Spyder environment because it allows you to visualize your work nicely (and it has a familiar design if you are coming from MATLAB).
ONWARD TO Level 1: Manipulating Variables
ONWARD TO Level 2: Conditionals and loops
ONWARD TO Level 3: PsychoPy 101
ONWARD TO Level 4: PsychoPy - Showing windows and stimuli
ONWARD TO Level 5: PsychoPy - Clocks and timing
ONWARD TO Level 6: PsychoPy - Response collection and saving data
ONWARD TO Advanced: Miscellanious tricks for improving your code
Before you can start programming your own experiment, you have to be comfortable with a syntax and grammar that computers can understand. If you are completely unfamiliar to programming, you're in luck! Python is a relatively easy language to learn.
Let's begin with the print function:
print("I love python!")
As of Python3, you have to put your text (called a string) in parentheses, or else it throws an error, like this:
print "I love python!"
At least python will tell you where you screwed up most of the time.
You can use single quotations or double quotations to write text to the screen, but you will also get an error if you forget to put your text in quotations:
print(I love python!)
Python can do all kind of basic mathematical operations, such as:
print(1 + 1) #addition
print(7 - 4) #subtraction
print(10.0 / 2.0) #division (NOTE: if you do not want to round to the nearest whole number,
#use floats instead of integers)
print(7 % 9) #modulo
print(11 * 1) #multiplication
print(2 ** 4) #exponentiation
Notice that the "#" symbol is what you can use to comment your script. Comments are your notes, usually telling you what a line means or why you wrote it. Anything following "#" (on the same line) will be ignored when you run your script. It is very important to always comment your script!!
You don't need spaces between individual symbols:
print(7-4)
An important part of python is the variable, which allows you to categorize your data with different labels. A simple variable can contain a single value (number or text):
subject_nr = 2
subject_name = 'sub2'
Variables do not automatically show up in your output without "print". You can print variables in different ways:
print(subject_nr)
print(subject_name)
print(subject_name,subject_nr)
print(subject_nr,subject_nr,subject_nr)
Once a variable is assigned in a script, it is remembered until you close your session. When you are programming an experiment, you can call a single variable multiple times throughout your script. You can also reassign a variable:
subject_nr = 3
print(subject_nr)
Reassigning a variable can be very useful, for example, when you want to cycle through experimental trials (trial=1, trial=2, etc.). Be careful that you don't reassign variables accidentally, and always give your variables sensical names.
Your variable can also be a boolean, which is a truth value (True or False):
fullscreen = False
print(fullscreen)
Booleans are useful for letting your experiment know whether to go on depending on if certain conditions are met. For example, you can program the experiment so that it must be in fullscreen mode to begin. To check whether you are in fullscreen mode, you can use the comparison operator "==" ("is equal to"):
print(fullscreen == True)
"fullscreen == True", in plain English, means: "Are we in fullscreen mode?" The program's answer is, "Nah".
You can also check the truth value of a variable using boolean expressions: == < > <= >= !=:
print(3 == 4)
print(4 < 3)
print(3 <= 3)
print(3 != 3)
In this case, you are asking, "Is 3 equal to 4?", "Is 4 less than 3?", "Is 3 less than or equal to 3?", "Is 3 not equal to 3?"
Booleans also have operators (and, or, not) that can be useful for experiments:
Let's start with and:
print(3 > 2 and 1 > 2)
In this case, you are asking "Are both statements true?" The "and" operator requires both statements to be true to get a "True" output. Compare this to or:
print(3 > 2 or 1 > 2)
The "or" operator only needs one of the statements to be true to get a "True" output. Here we are asking, "Is at least one of the statements true?"
Finally, we have not:
print(1 > 2)
print(not 1 > 2)
With "not", we are asking "Is 1 > 2 not true?" The answer? True!
You can also use combined operators, such as and not:
print(3 > 2 and not 1 > 2)
Here you are asking, "Is 3 greater than 2 and 1 is not greater than 2?"
Variables can be composed of several values, which can be expressed as a list. Lists are contained in square brackets [ ] and individual items are always separated by commas:
subject_nrs = [1,2,3,4,5]
subject_names = ['sub1','sub2','sub3','sub4','sub5']
responses = [3, 'yes', '1.0', True]
exp = 'my_exp'
print(subject_nrs)
print(subject_names)
print(responses)
print(exp)
As you can see, lists can be combinations of different types of values.
Instead of typing out all the items you want in your list, you can use certain functions to create lists. For example, "range":
trials=range(10)
print(trials)
As you can see, simply printing "trials" does not produce the list as of python3. It gives you the range of numbers that the variable "trials" contains (0,10). This means that the list starts with 0 (python indexing begins at 0), and stops before 10 -- in python, this is very important! 9 is the final number in the list. We know that "trials" is an actual list of 10 numbers if we check it's length using "len":
print(len(trials))
We know that "trials" contains a list of 10 numbers, so how do we see them? You either have to use the "list" function specifically to give you the range of numbers, or you can define the range as a list in one line:
trials=(range(10))
print(list(trials))
trials=list(range(10))
print(trials)
As you can see, range(10) gives us a list of integers between 0 and 10, starting at 0 and ending with 9. We can also define a range from a number other than 0, in which case we have to specify the starting number following "range":
trials = list(range(2,10))
print(trials)
There are lots of other functions associated with lists. For example:
maxlist = max(trials) #finds the maximum number in the list
minlist = min(trials) #finds the minimum number in the list
sumlist = sum(trials) #finds the sum of all numbers in the list
print(maxlist)
print(minlist)
print(sumlist)
Multiple variables can be stored in a dictionary. All the variables in a dictionary must be wrapped with { } brackets. Each variable must be preceded by a text label and a colon. Each variable is separated by a comma. Here is an example:
my_dict = {'numsubs':subject_nrs, 'subnames':subject_names, 'responses':responses, 'exp':exp}
print(my_dict)
Here you can see a few things: first, variables in the dictionary are not saved in the order they are defined (this is important for indexing, which we will get to in the next part of the tutorial). Second, variables can be different lengths and contain different kinds of information (lists, integers, tuples, etc.). Third, the text label does not have to match the variable name.
Dictionaries are the main format for storing your behavioral data in python.
The final important data type for our purposes is the array. To create arrays in python, we must first import the numpy library in our script. Python does not have an in-built function to create arrays so we will always use numpy. You can then create an array like this:
import numpy
data = numpy.array([1,2,3,4,5])
print(data)
print(type(data))
A numpy array looks a lot like a list (as of Summer 2020, it looks exactly like a list), but as you can see, if you ask python to tell you what type of variable you have with "type(data)", it will give you a numpy.ndarray. Because we work with so many arrays in our scripts, it is good to give numpy an acronym so we don't have to type the whole word out every time we use the library. Numpy is shortened to "np", like this:
import numpy as np
Then we create our array by directing python to the numpy library using the acronym we defined:
data = np.array([1,2,3,4,5])
print(data)
print(type(data))
As you can see, we created the same array, but with the np acronym. Once the numpy library is imported at the beginning of our script with "import numpy as np", we can continue to use this acronym throughout our script without redefining it.
array2 = np.array([0,0,0,0,0])
print(array2)
So what can we do with an array that we can't do with a list? Here are a few things...
np.linspace(starting value, ending value, total values):
print(np.linspace(0, 10, 5))
"linspace" creates an array of values evenly spaced between two values. In parentheses, your starting value comes first, then your ending value, then the total number of values to output.
print(np.linspace(2,10,5))
np.logspace(starting value power of 10, ending value power of 10, total values):
print(np.logspace(1,3,10))
"logspace" creates an array of values evenly spaced between two powers of 10 (in this case 1 and 3).
np.arange(start, stop, steps):
print(np.arange(2,20,4))
"arange" is similar to the "range" function with lists, in which you define the start and stop points (the fnial value will come before the stop point), followed by the steps. In this example, we produce an array of numbers between 2 and 20 in steps of 4.
np.zeros(number of zeros to produce):
nullarray = np.zeros(10)
print(nullarray)
You can also define an array of zeros with a particular shape:
np.zeros(shape=(rows, columns)):
null2 = np.zeros(shape=(5,3))
print(null2)
print(np.shape(null2))
You can do the same thing with ones by using np.ones:
onearray = np.ones(shape=(5,3))
print(onearray)