-
Notifications
You must be signed in to change notification settings - Fork 21
/
ppc1_python_intro.py
563 lines (464 loc) · 20.7 KB
/
ppc1_python_intro.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
# -*- coding: utf-8 -*-
# All scripts starts with the line above. It allows special characters
# to be used in the script without python crashing! E.g. æøåöôé$¤£@§½.
"""
Some python syntax that is relevant for coding psychopy experiments.
The "print" command is used a lot here just to let you look into the machinery
of the code. We're going to use print a lot when building scripts as a diagnostic
tool. But once everything works, we remove them to avoid taking up unnecessary
code / console space / computational resources.
This script runs in way less than 50 ms (a 20th of a second). Base python
is really fast!
Jonas Lindeløv, 2015
"""
print """
--------- VARIABLES AND PRINTING -----------
We're going to rely heavily on variables. We're going to specify all values
in the beginning of our experimental scripts and then use these values throughout
the experiment. In addition, the "print" command is very helpful for diagnosing.
"""
#print theBigElephant # wouldn't work because it's undefined here
#print globals().keys() # pre-defined variables
the_big_elephant = 5 # variables can have all sorts of crazy names
print the_big_elephant # print is your friend!
A_GOAT = 'hello'
aSparrow = A_GOAT + ' mister perfect' # concatenating (or "adding") strings
print aSparrow
print 'hello' + ' mister perfect'
print the_big_elephant + 4
print A_GOAT, aSparrow, the_big_elephant + 4, ['and', 'a', 'list']
a, b, c = 1, 2, 3
print c, b, a
print type(the_big_elephant), type(A_GOAT), type([1,2,3]) # assess type of variable
print bool, int, float, str, list, tuple, dict # built-in types of variables (objects)
print 'æøå', u'æøå' # "u" in front shows special characters appropriately
print 'These variables are defined now:', globals().keys()
"""
Exercise on variables and printing:
* define a variable and print it
* define a variable with the value 55 and another with the value 7. Then print
'one variable is 55 and the other variable is 7. The sum is 62 and the difference is 48'
... using the variable values.
* change the value of the variables and run the script again to verify that it works.
* try adding a string and an integer using +
Why is there an error?
try adding again, this time putting putting str() around the integer.
Why/how does it work?
* pro: explore what this does and play with it:
print 'the number %.2f and %i is %s' %(5.3455, 2.454, 'mighty fine')
"""
# SOLUTIONS:
fun = 5
print fun
number1 = 55
number2 = 7
print 'one variable is', number1, 'and the other variable is', number2, \
'. The sum is', number1 + number2, 'and the difference is', number1 - number2
#print 'five' + 5
print 'five' + str(5)
print """
----------- LISTS -----------
Lists are an ordered sequence of elements. Just like shopping lists.
The elements can be anything: numbers, strings, lists etc.
We're going to use lists a lot when generating a list of conditions and trials.
"""
# Indexing
my_list = [0, 'hello', [7,2], (1,4), {'goat': 5, 'fish': 2}]
print my_list
print my_list[0] # why 0? Because it means "shift 0 to the right"
print my_list[0] + my_list[2][1] + my_list[3][0] # 0 + 2 + 1
# Built-in operations
count = range(5) # [0, 1, 2, 3, 4]. Notice that range and index starts at zero.
print count
print sum(count), min(count), max(count), len(count) # works if all elements are numeric
print sum(count) / len(count) # cheap average
print count[0], count[-1] # first and last item
print count[:4], count[-4:] # first and last four items as list
# Modifying lists
count[2] = 99 # change index 2 (third element). Was previously a 2.
count += [11, 12, 13] # or: count.append(10) or count.append([10, 11, 12])
print count
"""
Exercise on lists:
* create a list with the numbers 11 through 100, including both.
* print the first and the last element
* print index 5 to 15.
* what is the mean of index 5 to 15?
* create a list with every fourth number between 200 and 266.
hint: google "python range" to see the documentation or use spyder :-)
* try creating a list with some strings.
* Append a few new strings using the list +=[new, fancy, elements] syntax.
* pro: make a list and explore sorted(), list.count() and list.remove()
* pro: range(10) + 5 doesn't work. Try this:
import numpy as np # numpy deals with matrices - like MATLAB
print np.array(range(10)) + 5 # viola!
Now try to multiply it with an int or maybe even another array? Is it
matrix multiplication or element-wise?
"""
# SOLUTIONS:
my_list = range(11, 101)
print my_list[0], my_list[-1]
my_string = 'hello world'
print my_string[0], my_string[-1]
print my_list[5:15]
print 'the mean is', sum(my_list[5:15]) / 10 # or len(my_list[5:15])
print range(200, 267, 4)
my_string_list = ['hello', 'python', 'coder']
my_string_list += ['way', 'to', 'go!']
print my_string_list
var = sorted(range(10), reverse=True)
print var.count(5)
print """
------------- DICTIONARIES ------------
Dictionaries are just like real dictionaries. You look up a keyword and
read out the value. There's no ordering so you just access values using keys!
I guess it's like an online dictionary.
We are going to represent a trial using a dictionary. The keys are the
parameters of the trial (duration, condition etc.) including the answers
that we get while running the experiment (reaction time, keys, score etc.).
"""
my_dict = {'first': 2, 'second': 4, 'third': [1, 2, 3], 'fourth': 'goat'}
print my_dict # notice that order doesn't matter
print my_dict['first']
print my_dict.keys() # a list of keys
print my_dict.values() # a list of values
# Modifying dictionaries
my_dict['somethingNew'] = 'a flower' # add a key-value pair
my_dict['second'] = 99 # if key already exists, just modify value
print my_dict
"""
Exercise on dictionaries and indexing:
* Make two dictionaries with the keys 'condition' and 'answer' and some values
* Make a list called "trial_list" with [dictonary1, dictionary2]
* Print the value of 'condition' in the second dict by indexing trial_list.
Hint: first index the list to get the dictionary and
then the dictionary to get the value.
* extend each of the two dictionaries with the key 'subject' and some values
hint: dict['newValue']=something or dict.update({'key':value}) or...
* pro: another way of "adding" multiple dictionaries in one line is
newDict = dict(dict1.items() + dict2.items()) # ... and so on
what does the .items() do and why can we add them?
"""
# SOLUTIONS
dict1 = {'condition': 'fun', 'answer': False}
dict2 = {'condition': 'boring', 'answer': True}
trial_list = [dict1, dict2]
print trial_list[1]['condition']
dict1.update({'subject': 'jonas'})
dict2.update({'subject': 'bodil'})
print dict(dict1.items() + dict2.items())
print """
---------- LOOPING ----------
Looping allows you to run the same code using different parameter(s).
We're going to loop over individual trials during the experiment.
We also pre-create the trials in a loop.
Finally, we time visual stimuli to monitor frames using a "flip-loop"
in which every iteration is synchronized to the monitor update.
"""
# for-loop
for i in range(4):
print 'iteration number', i
my_list = ['a', 'fancy', 'goat']
for i, word in enumerate(my_list):
print 'index', i, 'has the value', word
# cool syntax #1: for-else
for i in range(4):
if i == 5:
print 'that was a five!'
break
else:
print 'five not found'
# cool syntax #2: for-list (list comprehension)
fancy_goat = [i * 2 for i in [1, 5, 8]]
print fancy_goat
# Generate a list of trials!
trial_list = []
for condition in ['fun', 'meh', 'boring']:
for duration in [2, 4, 6]:
trial_list += [{'condition': condition, 'duration': duration}]
print trial_list
# while-loop.
counter = 1
while counter <= 4:
print 'still true on iteration number', counter
counter += 1
#if counter > 2:
# break # break stops the loop. You can use "while True" and then
# end the loop using break.
"""
Exercise on lists and loops:
* Make a for-loop over this list and print each word within the loop:
['just', 'looping', 'and', 'looping', 'and', 'looping']
* Now loop over trial_list which we defined earlier!
Outside (above) the loop, define a variable called "subjectID" with your surname.
Modify each trial within the loop so that each dictionary has a key
'subject' with your name (the value of subjectID).
* Add the keys 'ans' (answer) and 'rt' (reaction time) to each trial
(dictionary) with '' (empty string) as values.
trial['rt'] = ''
They are placeholders for the subject's answers when we run the experiment.
* Pros: make the same trial_list using the trial_list = [value for var in list] syntax.
hint: value is a dictionary
"""
# SOLUTIONS:
for a_fancy_goat in ['just', 'looping', 'and', 'looping', 'and', 'looping']:
print a_fancy_goat
subjectID = 'jonas'
for trial in trial_list:
trial['name'] = subjectID
trial['ans'] = ''
trial['rt'] = ''
print trial
# Method 1 (brief)
trial_list = [{'condition':condition, 'repetition':N} for condition in ['exiting', 'boring'] for N in range(10)]
print trial_list
# Method 2 (easier to understand)
trial_list = []
for condition in ['exciting', 'boring']:
for repetition in range(10):
trial_list += [{'condition': condition, 'repetition':repetition}]
print """
-------- LOGIC ----------
Logic is really simple. Something is either True of False. Nothing else.
Python logic is usually very easy to understand if you read it aloud. E.g.
"is 5 equal to 6?"
"is 8 smaller than 6 or larger than 6?"
We're mostly going to use logic to display stimuli differently based
on some condition. We're also going to use it to score trials.
"""
# keywords: True, False, if, elif, else
# and combination of logic: not, and, or, is
if True:
print 'True -->', True
if False:
print 'False -->', False
# prints the result of a lot of logical tests
print 'not True -->', not True
print 'True and false -->', True and False
print 'False or True -->', False or True
print '1 == 1 -->', 1 == 1
print '1 is 1 -->', 1 is 1
print '1 == 2 or 1 == 2 -->', 1 == 2 or 1 == 2
print 'the_big_elephant == 3 + 2 -->', the_big_elephant == 3 + 2
print '2 < 5 < 4 -->', 2 < 5 < 4
# Running code dependent on conditions
if False:
print 'false'
elif False or False:
print 'False or False'
else:
print 'else...'
# A short and convenient way to assign values dependent on conditions
my_test = 'horse' if 1 == 2 else 'goat' # useful for scoring trials!
my_test2 = 'horse' if 1 == 2 else 'goat' if False or True else 'sparrow'
print my_test, my_test2 # goat goat
"""
Exercise on logic:
* Is the following True or False?
5 < 8
* What about
5 < 8 and 5 + 2 == 6
* Then what about
5 < 8 and 5 + 2 == 6 or 'horse' == 'goat' or range(3) == [0, 1, 2]
* Define a variable called "do_test" and set it to True.
Then use an if-statement and run the above three tests if do_test is True
* Your participant responds either 'left', 'right' or something else.
Print different things depending on her response.
* Pros: determine whether empty lists, strings and dictionaries
are interpreted as False in a logical test.
Hint: You can coerce variables into boolean type using the bool() method.
"""
# SOLUTIONS
do_test = True
if do_test:
print 5 < 8
print 5 < 8 and 5 + 2 == 6
print 5 < 8 and 5 + 2 == 6 or 'horse' == 'goat' or range(3) == [0, 1, 2]
response = 'up'
if response == 'left':
print 'she pressed left!'
elif response == 'right':
print 'she pressed right!'
else:
print 'she pressed something else :-('
# Pro: the truth value of empty vs. non-empty stuff
print bool(''), bool('left')
print bool(0), bool(99)
print bool([]), bool([5,8])
print bool({}), bool({'hi':2})
print """
----- METHODS (FUNCTIONS) -------
Methods run a segment of code anywhere you'd like. It's useful for code-reuse and
it helps you to keep your code clean and consistent.
We're going to create two methods in the experiment. A runBlock(condition) and a
makeTrialList(condition).
"""
# a chunk of code that can be run using a single command, usually returning something
def add_plus_one(a, b):
a += 1
b += 1
return a + b
two_plus_three = add_plus_one(2, 3) # method returns 7 (2+1 + 3+1) and assigns to var
print two_plus_three
five_plus_ten = add_plus_one(5, 10) # 17
print five_plus_ten
add_plus_one(10, 10) # output is not assigned to anything and thus does nothing
# default values!
def add_plus_X(a, b=2, x=1):
return a + b + 2*x
print add_plus_X(1)
print add_plus_X(0, x=5)
print add_plus_X(5, 10) # no keywords uses default order
"""
Exercise on methods:
* Define a method that takes a string as input and returns the first
and last character of it as a single string.
Hint: indexing works on strings!
* Try printing the output.
* Save the output to a variable and print it.
* Define a method that takes a number as input and returns 'too low', 'just right'
or 'too high' depending on the size of the number. You choose the criteria!
* Modify your method so that you can specify the criteria as
arguments with default values.
* Pros: make a method "makeTrialList" that takes a condition as input
and returns a trial_list. You decide what kind of trials you want and
what difference the condition makes for the generation of the trial_list.
"""
# SOLUTIONS
def fancy_string(string):
return string[0], string[-1]
print fancy_string('Hello World')
def evaluate_magnitude(number, below=2, above=7):
if number < below:
return 'too low'
elif number > above:
return 'too high'
else:
return 'just right!'
print evaluate_magnitude(5)
print evaluate_magnitude(6, above=5)
# You can make multiline strings
common_mistakes = """
---------- COMMON MISTAKES ----------
Here is a list of simple common mistakes. Remember:
* every character counts. A single typo/ommision causes the script to crash.
* the script runs from the top and down. Things not defined yet cannot be referenced (used).
Python is really helpful to let you debug errors.
* in every error, the top line tells you the line at which the error
occurred. You probably mistyped something.
* in every error, the bottom line tells you the type of error. Examples:
* NameError: the variable is not defined above.
myVariable = 2
print myvariable # doesn't exist because python is case sensitive
* TypeError: the variables are of different types and you did something illegal.
my_list = [1,2,3]
myString = 'hello world'
print my_list + myString # TypeError
* SyntaxError: the line didn't follow the python syntax.
my Variable = 2 # SyntaxError. variables cannot contain spaces!
for i in range(10) # SyntaxError. forgot colon!
print i 'is a number' # SyntaxError. Space is not an operation!
* IndexError: you called a list index which does not exist.
list = [1,2,3]
print list[7] # IndexError
* KeyError: you called a dictionary key which does not exist.
dict = {'name': 'jonas'}
print dict['age'] # KeyError
* more generally, see http://i.imgur.com/WRuJV6r.png
* for the pros, see http://stackoverflow.com/questions/1011431/common-pitfalls-in-python
"""
"""
Exercise on errors:
Commit the errors above, just to see that it "works".
Note the type of error and line number of the error. Useful stuff for debugging!
"""
# There's a lot of useful built-in operations on each variable type. Let's
# play with the string above.
print common_mistakes.count('c') # counts number of small c's in the text above.
print common_mistakes.split('\n')[3] # a list of lines. Prints 4th line.
cm_words = common_mistakes.split(' ') # a list of words.
print cm_words[1].lower() # lowercase. prints 'common' instead of 'COMMON'
print """
--- COMBINING STUFF ------'
We often combine multiple sequential commands in one line. It works because
each command simply returns an output on which the next operation takes as input.
For example:
"""
print common_mistakes.split('\n')[1][2].capitalize().startswith('a')
"""
... returns False because:
* common_mistakes.split('\n') returns a list with individual lines.
* [1] gets the second line, "Here's a list of simple..."
* [2] gets the third character of that line, 'r'
* .capitalize() makes it capitalized, 'R'
* .startswith('a') tests whether the first character in 'R' equals 'a'.
i.e. 'R'[0] == 'C' --> False
"""
print '\n -------- MATH -----------'
print 5 / 2 # is 2 in python 2.x
print 5 / 2.0, float(5) / 2 # is 2.5 because there's a float. Tip: use "from __future__ import division"
print 1 + 2*2**3 - 1/5.0 # Python follows the order of operations
print 1 + (2*2)**(3 - 1) / 5.0 # Paranthesis prevail over other operations!
print 10 % 2 # modulus
# Style note: spacing in math should be used to make it easier to read by
# bringing primary operations closer together than lower operations.
print '\n ------ PICKING RANDOM ELEMENTS -------'
import random # import a module
funny_list = ['one', 'mighty', 'fine', 'horse']
print random.choice(funny_list) # picks one
print random.sample(funny_list, 3) # picks three
print random.sample(funny_list, len(funny_list)) # randomizes order!
"""
---------------------------------------------------------
If you found the above very challenging, just stop here.
The below code is for the geeks / over-achievers.
---------------------------------------------------------
"""
print '\n ------- NUMPY ----------'
# Numpy module contains a lot of useful stuff
import numpy as np
print np.sin(np.pi), np.cos(2 * np.pi)
print np.ceil(3.6), np.floor(3.6)
print np.log(np.exp(3)), np.log2(4) # logarithms
print np.abs(-5) # absolute value
# Randomness
print np.random.randint(3, 7, 5) # 5 random integers from [3, 4, 5, 6]
print np.random.randint(0, 2, 5) # 5 random 1's and 0's (True and False!)
print np.random.uniform(0, 1, 5) # 5 numbers between 0 and 1
print np.random.normal(5, 2, 5) # 5 numbers with mu=5, sd=2
# MATLAB like operations on full array (and matrix calculations)
# print range(10) + 5 # won't work in classical python
print np.array(range(10)) + 5 # [5, 6, ...]
print np.array([1,2,3]) * np.array([1,2,3]) # elementwise: [1, 4, 9]
print '\n --- SOME CROSS-TYPE CONSISTENSIES ---'
# Adding / extending using '+' sign
print 'hello' + 'world'
print ['hello'] + ['world']
print dict({'a':'hello'}.items() + {'b':'world'}.items())
# Emptiness and 0 is False. Anything else is True
print bool([]), bool(''), bool({}), bool(0) # emptiness and 0
print bool([0]), bool('False'), bool({'0': 0}), bool(-1)
# Counting number of elements using len() which is the same as .__len__()
print len('fun'), len([1,2,3]), len({'a':1, 'b':2, 'c':3}) # 3 3 3
print 'fun'.__len__(), [1,2,3].__len__(), {'a':1, 'b':2, 'c':3}.__len__() # 3 3 3
print '\n -- IDENTITY/REFERENCE VERSUS SIMILARITY --'
# "is" tests for reference and is more strict than "==" which tests for similarity.
# Identical primitives are the same and therefore similar.
print 99 is 99, 'a' is 'a', (5) is (5) # True True True
print 99 == 99, 'a' == 'a', (5) == (5) # True True True
# Similar lists, tuples and dicts are not the same
print [99] is [99], {'a': 4} is {'a': 4}, (5,2) is (5,2) # False False False
print [99] == [99], {'a': 4} == {'a': 4}, (5,2) == (5,2) # True True True
# The same is true for variables
goat = [1, 2, 3]
goat_reference = goat # reference to goat
goat_wannabe = [1, 2, 3] # just a similar object
goat_copy = goat[:] # useful way of copying a list!
print 'goat is goat1Reference -->', goat is goat_reference # True
print 'goat is goat1Wannabe -->', goat is goat_wannabe # False
print 'goat == goat_wannabe -->', goat == goat_wannabe # True
print 'goat is goat_copy -->', goat is goat_copy # False
print 'goat == goat_copy -->', goat == goat_copy # True
# Reference is really useful but also source of much confusion when changing referring variables
goat[2] = 'surprise!' # goat_reference is 'changed' here as well because it is simply a reference to goat
print goat, goat_reference, goat_wannabe