{ "metadata": { "name": "", "signature": "sha256:5a1df08d668a9ea60f833b7cba5b3bb9d1b16e75697957540f94fe3f92dd4971" }, "nbformat": 3, "nbformat_minor": 0, "worksheets": [ { "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "*Back to the main [index](../index.ipynb)*" ] }, { "cell_type": "heading", "level": 1, "metadata": {}, "source": [ "Streamline research with psychopy_ext" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "*Part of the introductory series [Python for Vision Researchers](http://gestaltrevision.be/wiki/python/python) brought to you by the [GestaltReVision](http://gestaltrevision.be) group (KU Leuven, Belgium).*\n", "\n", "In this part we introduce an advanced package, [psychopy_ext](http://psychopy_ext.klab.lt), that helps you tie together the entire research cycle. It is based on the following paper:\n", "\n", " Kubilius, J. (2014). A framework for streamlining research workflow in neuroscience and psychology. Frontiers in Neuroinformatics, 7, 52. doi:10.3389/fninf.2013.00052\n", "\n", "\n", "**Author:** [Jonas Kubilius](http://klab.lt) \n", "**Year:** 2014 \n", "**Copyright:** Public Domain as in [CC0](https://creativecommons.org/publicdomain/zero/1.0/) (except for figures that, technically speaking, need an attribution as in [CC BY](https://creativecommons.org/licenses/by/4.0/) because they are part of the publication mentioned above)" ] }, { "cell_type": "heading", "level": 1, "metadata": {}, "source": [ "Contents" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- [Introduction](Introduction)\n", " - [Reproducible research](#Reproducible-research)\n", " - [We need better tools!](#We-need-better-tools!)\n", " - [Introducing psychopy_ext](#Introducing-psychopy_ext)\n", "- [Step 1: Quick Demo](#Step-1:-Quick-Demo)\n", " - [Quick overview](#Quick-overview)\n", " - [More details of what it is doing](#More-details-of-what-it-is-doing)\n", " - [Why bother...](#Why-bother...)\n", " - [Quick exercises](#Quick-exercises)\n", "- [Step 2: The Change Blindness Experiment](#Step-2:-The-Change-Blindness-Experiment)\n", " - [Header: Importing modules](#Header:-Importing-modules)\n", " - [Initial user-defined information](#Initial-user-defined-information)\n", " - [Create window](#Create-window)\n", " - [Create stimuli](#Create-stimuli)\n", " - [Trial structure](#Trial-structure)\n", " - [Experimental plan](#Experimental-plan)\n", " - [Before trial](#Before-trial)\n", " - [Show stimuli](#Show-stimuli)\n", " - [And that's it!](#And-that's-it!)\n", " - [Change Detection Experiment: full code](#Change-Detection-Experiment:-full-code)\n", "- [Step 3: Data analysis](#Step-3:-Data-analysis)\n", " - [Reading in data](#Reading-in-data)\n", " - [Aggregating data](#Aggregating-data)\n", " - [Plotting](#Plotting) \n", "- [Step 4: Integrated development](#Step-4:-Integrated-development)\n", " - [More tasks](#More-tasks)\n", " - [More experiments](#More-experiments)\n", " - [GUI](#GUI)\n", " - [Command line interface](#Command-line-interface)\n", "- [Extra: Other features that will blow your mind](#Extra:-Other-features-that-will-blow-your-mind)\n", " - [Autorun](#Autorun)\n", " - [Analyzer](#Analyzer)\n", " - [Computer vision models included](#Computer-vision-models-included)\n", " - [Export your stimuli to vector graphics](#Export-your-stimuli-to-vector-graphics)" ] }, { "cell_type": "heading", "level": 1, "metadata": {}, "source": [ "Introduction" ] }, { "cell_type": "heading", "level": 2, "metadata": {}, "source": [ "Reproducible research" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "So far we've discussed how to code experiments. But research is more than just making an experiment! You have to analyze data, possibly also compare them to simulated data, present them in conferences and publish in journals. You should also nicely organize and verify your scripts. Ultimately, the goal would be to have your entire project completely reproducible, such that anybody could start from scratch and redo your experiments, regenerate your figures, posters, and papers, and directly build on your work -- this is how knowledge is accumulated and that is the whole Open Science concept that is taking over academia in recent years.\n", "\n", "I even made a figure to illustrate that:\n", "\n", "![Alt text](images/research_workflow.svg)\n", "\n", "So that is what you might want to do. But this is what you and I do instead:\n", "\n", "- \"My data? Um, here is the folder with the first pilot data, and there are also data from this intern here, but she never finished working on it. I'll have to look more closely which data was used...\"\n", "- \"I've got this analysis somewhere on an Excel spreadsheet, hold on...\"\n", "- \"Here is my SPSS analysis file! Oh, you don't have SPSS? Maybe you guys could get it through your department?\"\n", "- \"I've worked on this presentation for so long, and now my prof told me to run a couple more subjects. I'll have to redraw all my figures again!\"\n", "- \"Here's the zip file of all scripts for this project. It's a bit messy and without any comments, hope you manage to figure it out:) Oh, the main script doesn't run? Yeah, I see, you have to comment out several bits and also change Line 157 cause there seems to be a bug...\"\n", "\n", "**This is not reproducible at all.** We're too often relying on:\n", "\n", "- Too many tools that are not tied together in any way and would be hard to tie.\n", "- Commercial software when an equally robust free and open source option is available.\n", "- Tools that are not suited for academic work (yes, I'm looking at you, Excel -- see the Reinhart-Rogoff case)." ] }, { "cell_type": "heading", "level": 2, "metadata": {}, "source": [ "We need better tools!" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "I do not blame researchers for relying on all these ad-hoc solutions. While in theory it would be nice to code everything from A to Z, in practice we don't have the time to play these silly games. Why we don't have the time is a topic for a separate discussion, but why we have to play these silly games is the problem of software. Simply put, we the lack tools that would seamlessly enact good coding and sharing standards. We need **tools that act clever**.\n", "\n", "Technology rarely has this quality, unfortunately. If you don't agree with this, you obviously have never tried to explain a newbie how to run a Python script: \"OK, now run it. I mean, open the command line... it's in... um... OK, click on the Start button, type 'cmd', hit Enter. OK, now navigate to where there script is. OK, open Windows Explorer and get the path...\". This is not clever -- this is developers not caring. A smartphone that my grandfather cannot figure out is not clever -- it's pretentious.\n", "\n", "There is a reason why people stick to spreadsheets -- they're simple, intuitive and the data is *there*, as opposed to being only available when you run your analysis script ([Bret Victor's point](http://worrydream.com/MediaForThinkingTheUnthinkable/)). They're still stupid, of course -- have you ever tried making figures nice in Excel? -- and we rather want tools that:\n", "\n", "1. Have reasonable defaults, e.g., nice plots by default (hello, matplotlib with the 90's aesthetics)\n", "2. Require minimal user intervention, i.e., I don't want to spend hours making Python talk to R or fixing silly LaTeX bugs.\n", "3. Have an intuitive interface based on how people -- and not engineers -- think.\n", "4. Encourage good habits, just like in Python enforcing indentation -- which makes code more readable! -- is part of the syntax. Guido van Rossum was too experienced to leave it up to users who, frankly, rarely care." ] }, { "cell_type": "heading", "level": 2, "metadata": {}, "source": [ "Introducing `psychopy_ext`" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's build something better, something that would:\n", "\n", "1. Streamline as many workflow steps as possible (\"act clever\")\n", "2. Seamlessly tie together these workflow steps.\n", "3. Facilitate reproducibility of the entire workflow.\n", "\n", "Please give a warm welcome to [psychopy_ext](http://psychopy_ext.klab.lt), a package that has these aims in mind though probably does not live up to them quite yet. Psychopy_ext is nothing but a collection of wrapper scripts to a number useful packages:\n", "\n", "- PsychoPy is your bread and butter but you just want to never think again about saving data?\n", "- Love matplotlib but want nice outputs?\n", "- Were impressed by pandas but still don't know how to use it for computing accuracy?\n", "- Would use PyMVPA to analyze your fMRI data but don't know how to even get started?\n", "- Want to compare your data to outputs of computer vision models?\n", "\n", "Then **psychopy_ext is for you**!\n", "\n", "![Alt text](images/architecture.svg)\n", "\n", "Let's go through a simple demo to understand what it gives you." ] }, { "cell_type": "heading", "level": 1, "metadata": {}, "source": [ "Step 1: Quick Demo" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "*Note that in this demo we ``import fix`` so that the code could run from the notebook. In real life you don't do it and the ``Exp1`` inherits from ``exp.Experiment``.*" ] }, { "cell_type": "code", "collapsed": false, "input": [ "from psychopy import visual\n", "from psychopy_ext import exp\n", "\n", "from collections import OrderedDict\n", "\n", "import scripts.computer as computer\n", "PATHS = exp.set_paths('trivial', computer)\n", "\n", "class Exp1(exp.Experiment):\n", " \"\"\"\n", " Instructions (in reST format)\n", " =============================\n", "\n", " Press **spacebar** to start.\n", " \n", " **Hit 'j'** to advance to the next trial, *Left-Shift + Esc* to exit.\n", " \"\"\"\n", " def __init__(self,\n", " name='exp',\n", " info=OrderedDict([('subjid', 'quick_'),\n", " ('session', 1),\n", " ]),\n", " rp=None,\n", " actions='run'\n", " ):\n", " super(Exp1, self).__init__(name=name, info=info,\n", " rp=rp, actions=actions,\n", " paths=PATHS, computer=computer)\n", "\n", " # user-defined parameters\n", " self.ntrials = 8\n", " self.stimsize = 2 # in deg \n", "\n", " def create_stimuli(self):\n", " \"\"\"Define your stimuli here, store them in self.s\n", " \"\"\"\n", " self.create_fixation()\n", " self.s = {}\n", " self.s['fix']= self.fixation\n", " self.s['stim'] = visual.GratingStim(self.win, mask='gauss',\n", " size=self.stimsize)\n", "\n", " def create_trial(self):\n", " \"\"\"Define trial composition\n", " \"\"\"\n", " self.trial = [exp.Event(self,\n", " dur=.200, # in seconds\n", " display=[self.s['stim'], self.s['fix']],\n", " func=self.idle_event),\n", " exp.Event(self,\n", " dur=0,\n", " display=self.s['fix'],\n", " func=self.wait_until_response)\n", " ]\n", "\n", " def create_exp_plan(self):\n", " \"\"\"Put together trials\n", " \"\"\"\n", " exp_plan = []\n", " for trialno in range(self.ntrials):\n", " exp_plan.append(OrderedDict([\n", " ('trialno', trialno),\n", " ('onset', ''), # empty ones will be filled up\n", " ('dur', ''), # during runtime\n", " ('corr_resp', 1),\n", " ('subj_resp', ''),\n", " ('accuracy', ''),\n", " ('rt', ''),\n", " ]))\n", " self.exp_plan = exp_plan\n", "\n", "if __name__ == \"__main__\":\n", " Exp1(rp={'no_output':True, 'debug':True}).run()" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "\r", "trial 1" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\n" ] }, { "ename": "SystemExit", "evalue": "0", "output_type": "pyerr", "traceback": [ "An exception has occurred, use %tb to see the full traceback.\n", "\u001b[1;31mSystemExit\u001b[0m\u001b[1;31m:\u001b[0m 0\n" ] }, { "output_type": "stream", "stream": "stderr", "text": [ "To exit: use 'exit', 'quit', or Ctrl-D.\n" ] } ], "prompt_number": 2 }, { "cell_type": "heading", "level": 2, "metadata": {}, "source": [ "Quick overview" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Oopsies, that's complex! Let me parse that for you step-by-step:\n", "\n", "1. Import relevant modules, including computer parameters\n", "2. Define the experiment in a class\n", "3. Provide some info (instructions)\n", "4. Create stimuli\n", "5. Define the composition of a trial\n", "6. Define trial order and other info\n", "\n", "Here's a pic to illustrate that (focus on the `class Experiment` for now):\n", "\n", "![Alt text](images/scheme.svg)" ] }, { "cell_type": "heading", "level": 2, "metadata": {}, "source": [ "More details of what it is doing" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "1. Import modules that we need, such as psychopy and psychopy ext. There is also the `computer` module imported where parameters of your computer (screen size etc) are defined. Feel free to edit it.\n", "2. Define your experiment as a *class*. Why bother with classes? The major advantage is that you can then *inherit* methods from a basic template supplied with `psychopy_ext`. Then you only have to define or redefine methods that are not in that template. For example, looping through trials is in there, so if yu're happy with it, you don't have to write it again.\n", "3. Everything within the Experiment is a *method* for reasons mentioned above. OK, there's an extra advantage: you can divide your script into short, easily readable bits of code and give them meaningful names. I hope you agree that this code is actually better organized than what you do usually.\n", "4. First, you give some custom parameters, such as the default subjid, in `__init__()`. You can provide instructions how to run the experiment just above this method. If you format them using the [reST syntax](http://sphinx-doc.org/rest.html), as done in the example, it will render nicer-looking instructions.\n", "5. Next, you define your stimuli in `create_stimuli()`. A fancy fixation spot is available from `psychopy_ext`.\n", "6. Then you define the composition of your trial in `create_trial()`. Each trial is composed of a series of Events that have a particular duration, stimuli that need to be displayed, and a particular function describing what to do (e.g., how to present stimuli).\n", "7. Finally, you define all information that you need to run the experiment, such as the order of trials, in `create_exp_plan()` as a list of `dict` entries. Importantly, all the fields you provide here are written to the output file, and this is the only information that is written out. You can see that some fields, like accuracy, are empty. But they are filled in as the experiment progreses.\n", "\n", "And that is all you need to create a full experiment. OK, but where is `run()`? It's in the Experiment template so you don't have to do anything extra." ] }, { "cell_type": "heading", "level": 2, "metadata": {}, "source": [ "Why bother..." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It may seem that you could have easily written a similar experiment using the same old PsychoPy but don't underestimate how many things are happening behind the scenes:\n", "\n", "1. A log and data files are created and filled in.\n", "2. Everything is adjusted to the particular machine you are using (e.g., in the fMRI scanner you can define a different trigger key than the one you use in the experimental room).\n", "3. Looping through trials, trial durations and response collection is automatic too.\n", "\n", "And that's only the beginning!" ] }, { "cell_type": "heading", "level": 2, "metadata": {}, "source": [ "Quick exercises" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's make sure you understand how classes work. What is the output of the following code?" ] }, { "cell_type": "code", "collapsed": false, "input": [ "def myfunc():\n", " print 'stuff'\n", " \n", "class Output(object):\n", " def __init__(self):\n", " print 'init'\n", " def run(self):\n", " print 'run'" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 3 }, { "cell_type": "markdown", "metadata": {}, "source": [ "How about this one?" ] }, { "cell_type": "code", "collapsed": false, "input": [ "class Output(object):\n", " def __init__(self):\n", " print 'init'\n", " def run(self):\n", " print 'run'\n", " \n", "Output()" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 6, "text": [ "__main__.Output" ] } ], "prompt_number": 6 }, { "cell_type": "markdown", "metadata": {}, "source": [ "And this?" ] }, { "cell_type": "code", "collapsed": false, "input": [ "class Output(object):\n", " def __init__(self):\n", " print 'init'\n", " def go(self):\n", " print 'go'\n", " def run(self):\n", " print 'run'\n", " \n", "class Child(Output):\n", " def run(self):\n", " print 'child'\n", " \n", "Child().run()" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "init\n", "go\n" ] } ], "prompt_number": 7 }, { "cell_type": "heading", "level": 1, "metadata": {}, "source": [ "Step 2: The Change Blindness Experiment" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The best way to learn how to use ``psychopy_ext`` is to build your own experiment based on the demo above (or on more complex demos that come with the package). So let us reenact the Change Blindness Experiment from [Part 2](Part2_PsychoPy) using the ``psychopy_ext`` framework. It may be a good idea to keep both notebooks open as we are going to mostly copy/paste code." ] }, { "cell_type": "heading", "level": 2, "metadata": {}, "source": [ "Header: Importing modules" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The first thing, as usual, is to import all relevant modules. But note that since psychopy_ext extends PsychoPy, we don't have to import most of PsychoPy's modules as in Part 2." ] }, { "cell_type": "code", "collapsed": false, "input": [ "import numpy.random as rnd # for random number generators\n", "\n", "from psychopy import visual\n", "from psychopy_ext import exp\n", "\n", "from collections import OrderedDict\n", "\n", "import computer\n", "PATHS = exp.set_paths('.', computer) # '.' means that the root directory for saving outout is here\n", "PATHS['images'] = 'images'" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "all modules should be familiar more or less, except the mysterious ``computer``. Well, that's the user-defined module where settings specific to your computers are defined ([example settings are here](http://psychopy_ext.klab.lt/library/computer.html)). This is sper handy when you have several machines with different setups (e.g., one in your office, anoter in the testing room, and yet another at home for those of us who have no life).\n", "\n", "Also note that we set the paths where all output files are supposed to be saved. This is done to help you organize your project better. Since we set up paths here, it also makes sense to define the path to the images folder here too. (See the example below or check the default paths [here](http://psychopy_ext.klab.lt/library/experiment.html#setting-up-paths-set-paths).)\n", "\n", "![Alt text](images/tree.svg)" ] }, { "cell_type": "heading", "level": 2, "metadata": {}, "source": [ "Initial user-defined information" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Next we define the ``ChangeDet`` class with its properties. This class is derived from ``exp.Experiment`` which, in turn, is nothing but the same old ``TrialHandler``. Thus we ought to pass the relevant parameters here, as we do with ``method='sequential'``. (Other options that the ``__init__`` takes are explained in the [documentation](http://psychopy_ext.klab.lt/library/experiment.html#initialization-init).)\n", "\n", "The idea of ``__init__`` is to define all (I mean, **all**) parameters here so that you can easily find and change them later.\n", "\n", "There are several kinds of parameters you can define:\n", "\n", "- ``info``: parameters that you want a user to be able to change on the go, e.g., participant ID\n", "- ``rp``: parameters conrolling the behavior of the program that you want a user to be bale to change on the go, e.g., whether to save outout or not\n", "- Other parameters that an outside user should not change on the go but that define how the program works, e.g., the number of trials. Those are defined as ``self.var_name`` where ``self`` means these variables are shared within the ChangeDet class -- you can access them from any other function in that class.\n", "\n", "``info`` and ``rp`` are in fact used in a GUI similar to the dialog box we used before (but more elaborate). You don't have to create the GUI yourself -- it all happens automatically and we'll demonstrate that later.\n", "\n", "This is also where we define keys used to respond in the ``self.computer.valid_responses`` in the format ``{'key name': correct or incorrect response}``. By default, Shift+Esc is used for escape and spacebar to advance from instructions to testing, so here we only need to define what counts as a correct response to advance to the next trial. Since everything in this experiment is \"correct\", we set ``space':1``.\n", "\n", "Note that we're defining instructions right at the top here. That serve a twofold purpose. On the one hand, it is natural to explain the experiment that the rest of the code enacts. On the other hand, this is also the [docstring](https://en.wikipedia.org/wiki/Docstring) that is encouraged as a good programming practice, so you're documenting your code at the same time. Trying to act clever here!\n", "\n", "We're also omitting writing date string to the output file because ``psychopy_ext`` creates a log file (you'll see later) with all this information and more.\n", "\n", "Given all this information, ``psychopy_ext`` also automatically knows how to create output files and place them in a convenient location. So a large chunk of code is not necessary anymore." ] }, { "cell_type": "code", "collapsed": false, "input": [ "class ChangeDet(exp.Experiment):\n", " \"\"\"\n", " Change Detection Experiment\n", " ===========================\n", "\n", " In this experiment you will see photographs flickering with a tiny detail in them changing.\n", " Your task is to detect where the change is occuring.\n", " To make it harder, there are bubbles randomly covering the part of the photos.\n", "\n", " Hit **spacebar to begin**. When you detect a change, hit **spacebar** again.\n", " \"\"\"\n", " def __init__(self,\n", " name='exp',\n", " info=OrderedDict([('exp_name', 'Change Detection'),\n", " ('subjid', 'cd_'),\n", " ('gender', ('male', 'female')),\n", " ('age', 18),\n", " ('left-handed', False)\n", " ]),\n", " rp=None,\n", " actions='run',\n", " order='sequential'\n", " ):\n", " super(ChangeDet, self).__init__(name=name, info=info,\n", " rp=rp, actions=actions,\n", " paths=PATHS, computer=computer)\n", "\n", " # user-defined parameters\n", " self.imlist = ['1','2','3','4','5','6'] # image names without the suffixes\n", " self.asfx = 'a.jpg' # suffix for the first image\n", " self.bsfx = 'b.jpg' # suffix for the second image\n", " self.scrsize = (900, 600) # screen size in px\n", " self.stimsize = (9, 6) # stimulus size in degrees visual angle\n", " self.timelimit = 30 # sec\n", " self.n_bubbles = 40\n", " self.changetime = .500 #sec\n", "\n", " self.computer.valid_responses = {'space': 1}\n", "\n", " self.trial_instr = ('Press spacebar to start the trial.\\n\\n'\n", " 'Hit spacebar again when you detect a change.')" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "heading", "level": 2, "metadata": {}, "source": [ "Create window" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The window is usually created automatically for us, but in this particular case we want to be able to define its size so we have to override the particular window creation routine with our custom function. This example is also useful for you to see how to change the default behavior of `psychopy_ext`." ] }, { "cell_type": "code", "collapsed": false, "input": [ "def create_win(self, *args, **kwargs):\n", " super(ChangeDet, self).create_win(size=self.scrsize, units='deg',\n", " *args, **kwargs)" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "heading", "level": 2, "metadata": {}, "source": [ "Create stimuli" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Should be straightforward by now, except that all stimuli are kept in a ``dict`` called ``self.s``. Moreover, the window is defined in terms of degrees visual angle, so stimuli are implicitly using these units too." ] }, { "cell_type": "code", "collapsed": false, "input": [ "def create_stimuli(self):\n", " \"\"\"Define your stimuli here, store them in self.s\n", " \"\"\"\n", " self.s = {}\n", " self.s['bitmap1'] = visual.ImageStim(self.win, size=self.stimsize)\n", " self.s['bitmap2'] = visual.ImageStim(self.win, size=self.stimsize)\n", " self.s['bubble'] = visual.Circle(self.win, fillColor='black', lineColor='black')" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "heading", "level": 2, "metadata": {}, "source": [ "Trial structure" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Remember, each trial consists of events of a certain duration, and we can pass a custom function of what should be happening during the trial. Here we create structure with a single event that lasts the maximum duration (i.e., 30 sec) and call a custom function ``show_stim`` that will control flipping between images, drawing bubbles etc." ] }, { "cell_type": "code", "collapsed": false, "input": [ "def create_trial(self):\n", " \"\"\"Define trial composition\n", " \"\"\"\n", " self.trial = [exp.Event(self,\n", " dur=self.timelimit, # in seconds\n", " display=[self.s['bitmap1'], self.s['bitmap2'], self.s['bubble']],\n", " func=self.show_stim)\n", " ]" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "heading", "level": 2, "metadata": {}, "source": [ "Experimental plan" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Here we put all information about stimuli and so on that will be recorded in the output files." ] }, { "cell_type": "code", "collapsed": false, "input": [ "def create_exp_plan(self):\n", " \"\"\"Put together trials\n", " \"\"\"\n", " # Check if all images exist\n", " for im in self.imlist:\n", " if (not os.path.exists(os.path.join(self.paths['images'], im+self.asfx)) or\n", " not os.path.exists(os.path.join(self.paths['images'], im+self.bsfx))):\n", " raise Exception('Image files not found in image folder: ' + str(im))\n", "\n", " # Randomize the image order\n", " rnd.shuffle(self.imlist)\n", "\n", " # Create the orientations list: half upright, half inverted\n", " orilist = [0,1]*(len(self.imlist)/2)\n", "\n", " # Randomize the orientation order\n", " rnd.shuffle(orilist)\n", "\n", " exp_plan = []\n", " for im, ori in zip(self.imlist, orilist):\n", " exp_plan.append(OrderedDict([\n", " ('im', im),\n", " ('ori', ori),\n", " ('onset', ''), # empty ones will be filled up\n", " ('dur', ''), # during runtime\n", " ('corr_resp', 1),\n", " ('subj_resp', ''),\n", " ('accuracy', ''),\n", " ('rt', ''),\n", " ]))\n", " self.exp_plan = exp_plan" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "heading", "level": 2, "metadata": {}, "source": [ "Before trial" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We need to show instructions before each trial and decide whether stimuli will be upright or inverted. To be more efficient, we first load images (it may take some time) and only when that is ready, show instructions." ] }, { "cell_type": "code", "collapsed": false, "input": [ "def before_trial(self):\n", " \"\"\"Set up stimuli prior to a trial\n", " \"\"\"\n", " im_fname = os.path.join(self.paths['images'], self.this_trial['im'])\n", " self.s['bitmap1'].setImage(im_fname + self.asfx)\n", " self.s['bitmap1'].setOri(self.this_trial['ori'])\n", " self.s['bitmap2'].setImage(im_fname + self.bsfx)\n", " self.s['bitmap2'].setOri(self.this_trial['ori'])\n", " self.bitmap = self.s['bitmap1']\n", "\n", " if self.thisTrialN > 0: # no need for instructions for the first trial\n", " self.show_text(text=self.trial_instr, wait=0)" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "heading", "level": 2, "metadata": {}, "source": [ "Show stimuli" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Finally, we define what happens during each trial. It's mostly copy/paste from our previous implementation with one significant change: we use [`last_keypress()`](http://psychopy_ext.klab.lt/api/generated/psychopy_ext.exp.Task.last_keypress.html#psychopy_ext.exp.Task.last_keypress) function to record user responses. This function is aware of the keys that we accept as responses as well as about special keys, such as Shift+Esc for exit. We therefore do not have to then check manually if the participant pressed a spacebar or and exit key. Moreover, the information about responses needs to be passed further (for writing responses to files etc) thus we have to include the `return keys` statement at the end.\n", "\n", "Also notice that since everything is defined in terms of degrees visual angle, we have to adjust bubble size accordingly." ] }, { "cell_type": "code", "collapsed": false, "input": [ "def show_stim(self, *args, **kwargs):\n", " \"\"\"Control stimuli during the trial\n", " \"\"\"\n", " # Empty the keypresses list\n", " event.clearEvents()\n", " keys = []\n", " change_clock = core.Clock()\n", "\n", " # Start the trial\n", " # Stop trial if spacebar or escape has been pressed, or if 30s have passed\n", "\n", " while len(keys) == 0 and self.trial_clock.getTime() < self.this_event.dur:\n", " # Switch the image\n", " if self.bitmap == self.s['bitmap1']:\n", " self.bitmap = self.s['bitmap2']\n", " else:\n", " self.bitmap = self.s['bitmap1']\n", "\n", " self.bitmap.draw()\n", "\n", " # Draw bubbles of increasing radius at random positions\n", " for radius in range(self.n_bubbles):\n", " self.s['bubble'].setRadius(radius/100.)\n", " self.s['bubble'].setPos(((rnd.random()-.5) * self.stimsize[0],\n", " (rnd.random()-.5) * self.stimsize[1] ))\n", " self.s['bubble'].draw()\n", "\n", " # Show the new screen we've drawn\n", " self.win.flip()\n", "\n", " # For the duration of 'changetime',\n", " # Listen for a spacebar or escape press\n", "\n", " change_clock.reset()\n", " while change_clock.getTime() <= self.changetime:\n", " keys = self.last_keypress(keyList=self.computer.valid_responses.keys(),\n", " timeStamped=self.trial_clock)\n", "\n", " if len(keys) > 0:\n", " print keys\n", " break\n", " return keys" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "heading", "level": 2, "metadata": {}, "source": [ "And that's it!" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Notice that you did not have to do many things here anymore:\n", "\n", "- Define the window and its properties\n", "- Define trial loop\n", "- Define output files and write to them\n", "- Define log files that record what happens during the experiment, including errors\n", "- Catch escapes\n", "- Deal with instructions at the beginning and end" ] }, { "cell_type": "heading", "level": 2, "metadata": {}, "source": [ "Change Detection Experiment: full code" ] }, { "cell_type": "code", "collapsed": false, "input": [ "%load scripts/changedet.py" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 2 }, { "cell_type": "code", "collapsed": false, "input": [ "import os\n", "\n", "import numpy.random as rnd # for random number generators\n", "\n", "from psychopy import visual, core, event\n", "from psychopy_ext import exp\n", "\n", "from collections import OrderedDict\n", "\n", "import scripts.computer as computer\n", "PATHS = exp.set_paths('change_detection', computer)\n", "PATHS['images'] = '../Part2/images/'\n", "\n", "\n", "class ChangeDet(exp.Experiment):\n", " \"\"\"\n", " Change Detection Experiment\n", " ===========================\n", "\n", " In this experiment you will see photographs flickering with a tiny detail in them changing.\n", " Your task is to detect where the change is occuring.\n", " To make it harder, there are bubbles randomly covering the part of the photos.\n", "\n", " Hit **spacebar to begin**. When you detect a change, hit **spacebar** again.\n", " \"\"\"\n", " def __init__(self,\n", " name='exp',\n", " info=OrderedDict([('exp_name', 'Change Detection'),\n", " ('subjid', 'cd_'),\n", " ('gender', ('male', 'female')),\n", " ('age', 18),\n", " ('left-handed', False)\n", " ]),\n", " rp=None,\n", " actions='run',\n", " order='sequential'\n", " ):\n", " super(ChangeDet, self).__init__(name=name, info=info,\n", " rp=rp, actions=actions,\n", " paths=PATHS, computer=computer)\n", "\n", " # user-defined parameters\n", " self.imlist = ['1','2','3','4','5','6'] # image names without the suffixes\n", " self.asfx = 'a.jpg' # suffix for the first image\n", " self.bsfx = 'b.jpg' # suffix for the second image\n", " self.scrsize = (900, 600) # screen size in px\n", " self.stimsize = (9, 6) # stimulus size in degrees visual angle\n", " self.timelimit = 30 # sec\n", " self.n_bubbles = 40\n", " self.changetime = .500 #sec\n", "\n", " self.computer.valid_responses = {'space': 1}\n", "\n", " self.trial_instr = ('Press spacebar to start the trial.\\n\\n'\n", " 'Hit spacebar again when you detect a change.')\n", "\n", " def create_win(self, *args, **kwargs):\n", " super(ChangeDet, self).create_win(size=self.scrsize, units='deg',\n", " *args, **kwargs)\n", "\n", " def create_stimuli(self):\n", " \"\"\"Define your stimuli here, store them in self.s\n", " \"\"\"\n", " self.s = {}\n", " self.s['bitmap1'] = visual.ImageStim(self.win, size=self.stimsize)\n", " self.s['bitmap2'] = visual.ImageStim(self.win, size=self.stimsize)\n", " self.s['bubble'] = visual.Circle(self.win, fillColor='black', lineColor='black')\n", "\n", " def create_trial(self):\n", " \"\"\"Define trial composition\n", " \"\"\"\n", " self.trial = [exp.Event(self,\n", " dur=self.timelimit, # in seconds\n", " display=[self.s['bitmap1'], self.s['bitmap2']],\n", " func=self.show_stim)\n", " ]\n", "\n", " def create_exp_plan(self):\n", " \"\"\"Put together trials\n", " \"\"\"\n", " # Check if all images exist\n", " for im in self.imlist:\n", " if (not os.path.exists(os.path.join(self.paths['images'], im+self.asfx)) or\n", " not os.path.exists(os.path.join(self.paths['images'], im+self.bsfx))):\n", " raise Exception('Image files not found in image folder: ' + str(im))\n", "\n", " # Randomize the image order\n", " rnd.shuffle(self.imlist)\n", "\n", " # Create the orientations list: half upright, half inverted\n", " orilist = [0,180]*(len(self.imlist)/2)\n", "\n", " # Randomize the orientation order\n", " rnd.shuffle(orilist)\n", "\n", " exp_plan = []\n", " for trialno, (im, ori) in enumerate(zip(self.imlist, orilist)):\n", " exp_plan.append(OrderedDict([\n", " ('im', im),\n", " ('ori', ori),\n", " ('onset', ''), # empty ones will be filled up\n", " ('dur', ''), # during runtime\n", " ('corr_resp', 1),\n", " ('subj_resp', ''),\n", " ('accuracy', ''),\n", " ('rt', ''),\n", " ]))\n", " self.exp_plan = exp_plan\n", "\n", " def before_trial(self):\n", " \"\"\"Set up stimuli prior to a trial\n", " \"\"\"\n", " im_fname = os.path.join(self.paths['images'], self.this_trial['im'])\n", " self.s['bitmap1'].setImage(im_fname + self.asfx)\n", " self.s['bitmap1'].setOri(self.this_trial['ori'])\n", " self.s['bitmap2'].setImage(im_fname + self.bsfx)\n", " self.s['bitmap2'].setOri(self.this_trial['ori'])\n", " self.bitmap = self.s['bitmap1']\n", "\n", " if self.thisTrialN > 0: # no need for instructions for the first trial\n", " self.show_text(text=self.trial_instr, wait=0)\n", "\n", " def show_stim(self, *args, **kwargs):\n", " \"\"\"Control stimuli during the trial\n", " \"\"\"\n", " # Empty the keypresses list\n", " event.clearEvents()\n", " keys = []\n", " change_clock = core.Clock()\n", "\n", " # Start the trial\n", " # Stop trial if spacebar or escape has been pressed, or if 30s have passed\n", "\n", " while len(keys) == 0 and self.trial_clock.getTime() < self.this_event.dur:\n", " # Switch the image\n", " if self.bitmap == self.s['bitmap1']:\n", " self.bitmap = self.s['bitmap2']\n", " else:\n", " self.bitmap = self.s['bitmap1']\n", "\n", " self.bitmap.draw()\n", "\n", " # Draw bubbles of increasing radius at random positions\n", " for radius in range(self.n_bubbles):\n", " self.s['bubble'].setRadius(radius/100.)\n", " self.s['bubble'].setPos(((rnd.random()-.5) * self.stimsize[0],\n", " (rnd.random()-.5) * self.stimsize[1] ))\n", " self.s['bubble'].draw()\n", "\n", " # Show the new screen we've drawn\n", " self.win.flip()\n", "\n", " # For the duration of 'changetime',\n", " # Listen for a spacebar or escape press\n", "\n", " change_clock.reset()\n", " while change_clock.getTime() <= self.changetime:\n", " keys = self.last_keypress(keyList=self.computer.valid_responses.keys(),\n", " timeStamped=self.trial_clock)\n", "\n", " if len(keys) > 0:\n", " print keys\n", " break\n", " return keys\n", "\n", "if __name__ == \"__main__\":\n", " ChangeDet(rp={'no_output':True, 'debug':True}).run()\n" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "heading", "level": 1, "metadata": {}, "source": [ "Step 3: Data analysis" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`pscyhopy_ext` is not meant only for helping to run experiments. As we discussed above, there are many other tasks that a researcher needs to do. One of them is data analysis. You may be used to doing it in Excel or SPSS, or R, but Python is actually sufficient to carry out many simple and more complex analyses. And it may also be nice to have your experimental and analysis code together in a single file.\n", "\n", "There is the [`pandas`](http://pandas.pydata.org/) package in Python offering great data analysis capabilites. `psychopy_ext` wraps it with the `stats` and `plot` modules to help you do typical analyses efficiently. For more power, you may want to use [`statsmodels`](http://statsmodels.sourceforge.net/).\n", "\n", "So let's look at how to analyze data from your experiment. For this example, we will use data from a paper by [de-Wit, Kubilius et al. (2013)](http://dx.doi.org/10.1068/i0613rep)." ] }, { "cell_type": "heading", "level": 2, "metadata": {}, "source": [ "Reading in data" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Reading in data is done by a clever `read_csv` method that can get datga both from local sources (your computer) and the internet. In this example, we fecth data for 12 control participants (so that is twelve files) and concatenate them together into a single large structure, called a DataFrame, as seen in the output." ] }, { "cell_type": "code", "collapsed": false, "input": [ "import pandas\n", "\n", "# get data from de-Wit, Kubilius et al. (2013); will take some time\n", "path = 'https://bitbucket.org/qbilius/df/raw/aed0ac3eba09d1d688e87816069f5b05e127519e/data/controls2_%02d.csv'\n", "data = [pandas.read_csv(path % i) for i in range(1,13)]\n", "df = pandas.concat(data, ignore_index=True)\n", "df" ], "language": "python", "metadata": {}, "outputs": [ { "html": [ "
\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
expNamesubjIDrunNorunTypeparaTypeparaNameblockNostartBlocktrialNocondonsetactualOnsetdurcontextposcorrRespsubjRespaccuracyRT
0 run controls2_01 1 main event para06 0 1 0 7 0.0 0.004020 0.3 Whole Bottom Left 3 3 Correct 1691.731086
1 run controls2_01 1 main event para06 1 1 1 1 0.3 1.693590 0.3 Parts Top Left 1 4 Incorrect 1847.628840
2 run controls2_01 1 main event para06 2 1 2 1 0.6 3.541586 0.3 Parts Top Left 1 1 Correct 1663.642791
3 run controls2_01 1 main event para06 3 1 3 6 0.9 5.205333 0.3 Whole Top Right 2 2 Correct 1511.654446
4 run controls2_01 1 main event para06 4 1 4 1 1.2 6.717345 0.3 Parts Top Left 1 1 Correct 1959.769867
5 run controls2_01 1 main event para06 5 1 5 1 1.5 8.677413 0.3 Parts Top Left 1 1 Correct 1335.666005
6 run controls2_01 1 main event para06 6 1 6 8 1.8 10.013345 0.3 Whole Bottom Right 4 4 Correct 1495.693448
7 run controls2_01 1 main event para06 7 1 7 3 2.1 11.509252 0.3 Parts Bottom Left 3 3 Correct 2319.651968
8 run controls2_01 1 main event para06 8 1 8 7 2.4 13.829205 0.3 Whole Bottom Left 3 3 Correct 1919.638110
9 run controls2_01 1 main event para06 9 1 9 5 2.7 15.749133 0.3 Whole Top Left 1 1 Correct 1463.658959
10 run controls2_01 1 main event para06 10 1 10 7 3.0 17.213078 0.3 Whole Bottom Left 3 3 Correct 1335.654028
11 run controls2_01 1 main event para06 11 1 11 8 3.3 18.549068 0.3 Whole Bottom Right 4 4 Correct 1495.674200
12 run controls2_01 1 main event para06 12 1 12 4 3.6 20.044989 0.3 Parts Bottom Right 4 4 Correct 2079.699771
13 run controls2_01 1 main event para06 13 1 13 5 3.9 22.125191 0.3 Whole Top Left 1 1 Correct 1319.653252
14 run controls2_01 1 main event para06 14 1 14 3 4.2 23.444928 0.3 Parts Bottom Left 3 4 Incorrect 1967.667815
15 run controls2_01 1 main event para06 15 1 15 3 4.5 25.412903 0.3 Parts Bottom Left 3 3 Correct 1455.684448
16 run controls2_01 1 main event para06 16 1 16 1 4.8 26.868817 0.3 Parts Top Left 1 4 Incorrect 2111.706457
17 run controls2_01 1 main event para06 17 1 17 4 5.1 28.980800 0.3 Parts Bottom Right 4 1 Incorrect 1575.592539
18 run controls2_01 1 main event para06 18 1 18 1 5.4 30.556723 0.3 Parts Top Left 1 1 Correct 2583.716753
19 run controls2_01 1 main event para06 19 1 19 5 5.7 33.140642 0.3 Whole Top Left 1 1 Correct 1383.702553
20 run controls2_01 1 main event para06 20 1 20 4 6.0 34.524658 0.3 Parts Bottom Right 4 4 Correct 1615.689649
21 run controls2_01 1 main event para06 21 1 21 6 6.3 36.140685 0.3 Whole Top Right 2 2 Correct 1399.671678
22 run controls2_01 1 main event para06 22 1 22 8 6.6 37.540624 0.3 Whole Bottom Right 4 4 Correct 1631.644232
23 run controls2_01 1 main event para06 23 1 23 5 6.9 39.172514 0.3 Whole Top Left 1 1 Correct 1423.674768
24 run controls2_01 1 main event para06 24 1 24 2 7.2 40.596501 0.3 Parts Top Right 2 3 Incorrect 2207.632844
25 run controls2_01 1 main event para06 25 1 25 5 7.5 42.804405 0.3 Whole Top Left 1 1 Correct 1447.695395
26 run controls2_01 1 main event para06 26 1 26 6 7.8 44.252363 0.3 Whole Top Right 2 2 Correct 1447.696678
27 run controls2_01 1 main event para06 27 1 27 8 8.1 45.700631 0.3 Whole Bottom Right 4 4 Correct 1303.450589
28 run controls2_01 1 main event para06 28 1 28 0 8.4 47.004304 0.3 Fixation NaNNaNNaN Correct NaN
29 run controls2_01 1 main event para06 29 1 29 0 8.7 47.304275 0.3 Fixation NaNNaNNaN Correct NaN
30 run controls2_01 1 main event para06 30 1 30 0 9.0 47.604785 0.3 Fixation NaNNaNNaN Correct NaN
31 run controls2_01 1 main event para06 31 1 31 6 9.3 47.904776 0.3 Whole Top Right 2 2 Correct 1427.156452
32 run controls2_01 1 main event para06 32 1 32 5 9.6 49.332309 0.3 Whole Top Left 1 1 Correct 1287.643999
33 run controls2_01 1 main event para06 33 1 33 8 9.9 50.620247 0.3 Whole Bottom Right 4 4 Correct 1287.665385
34 run controls2_01 1 main event para06 34 1 34 2 10.2 51.908175 0.3 Parts Top Right 2 2 Correct 1991.666627
35 run controls2_01 1 main event para06 35 1 35 3 10.5 53.900168 0.3 Parts Bottom Left 3 3 Correct 1487.696267
36 run controls2_01 1 main event para06 36 1 36 1 10.8 55.388153 0.3 Parts Top Left 1 4 Incorrect 1607.679209
37 run controls2_01 1 main event para06 37 1 37 5 11.1 56.996092 0.3 Whole Top Left 1 1 Correct 1495.672061
38 run controls2_01 1 main event para06 38 1 38 3 11.4 58.492151 0.3 Parts Bottom Left 3 3 Correct 2031.672205
39 run controls2_01 1 main event para06 39 1 39 4 11.7 60.523999 0.3 Parts Bottom Right 4 3 Incorrect 3711.614753
40 run controls2_01 1 main event para06 40 1 40 2 12.0 64.235910 0.3 Parts Top Right 2 2 Correct 1815.614455
41 run controls2_01 1 main event para06 41 1 41 1 12.3 66.051824 0.3 Parts Top Left 1 4 Incorrect 1759.731286
42 run controls2_01 1 main event para06 42 1 42 4 12.6 67.811819 0.3 Parts Bottom Right 4 1 Incorrect 1607.676643
43 run controls2_01 1 main event para06 43 1 43 7 12.9 69.419797 0.3 Whole Bottom Left 3 3 Correct 1303.664879
44 run controls2_01 1 main event para06 44 1 44 7 13.2 70.723707 0.3 Whole Bottom Left 3 3 Correct 1359.686204
45 run controls2_01 1 main event para06 45 1 45 0 13.5 72.083681 0.3 Fixation NaNNaNNaN Correct NaN
46 run controls2_01 1 main event para06 46 1 46 0 13.8 72.383803 0.3 Fixation NaNNaNNaN Correct NaN
47 run controls2_01 1 main event para06 47 1 47 3 14.1 72.684001 0.3 Parts Bottom Left 3 3 Correct 2583.301432
48 run controls2_01 1 main event para06 48 1 48 0 14.4 75.267621 0.3 Fixation NaNNaNNaN Correct NaN
49 run controls2_01 1 main event para06 49 1 49 4 14.7 75.567601 0.3 Parts Bottom Right 4 3 Incorrect 3435.445341
50 run controls2_01 1 main event para06 50 1 50 8 15.0 79.003531 0.3 Whole Bottom Right 4 4 Correct 1655.706348
51 run controls2_01 1 main event para06 51 1 51 7 15.3 80.659547 0.3 Whole Bottom Left 3 3 Correct 1415.669888
52 run controls2_01 1 main event para06 52 1 52 6 15.6 82.075451 0.3 Whole Top Right 2 2 Correct 1455.673755
53 run controls2_01 1 main event para06 53 1 53 7 15.9 83.531444 0.3 Whole Bottom Left 3 3 Correct 1311.654360
54 run controls2_01 1 main event para06 54 1 54 2 16.2 84.843354 0.3 Parts Top Right 2 2 Correct 1751.674652
55 run controls2_01 1 main event para06 55 1 55 1 16.5 86.595483 0.3 Parts Top Left 1 4 Incorrect 1639.529776
56 run controls2_01 1 main event para06 56 1 56 1 16.8 88.235367 0.3 Parts Top Left 1 1 Correct 2151.664557
57 run controls2_01 1 main event para06 57 1 57 2 17.1 90.387212 0.3 Parts Top Right 2 2 Correct 2087.715344
58 run controls2_01 1 main event para06 58 1 58 0 17.4 92.475212 0.3 Fixation NaNNaNNaN Correct NaN
59 run controls2_01 1 main event para06 59 1 59 0 17.7 92.775342 0.3 Fixation NaNNaNNaN Correct NaN
.........................................................
\n", "

1920 rows \u00d7 19 columns

\n", "
" ], "metadata": {}, "output_type": "pyout", "prompt_number": 2, "text": [ " expName subjID runNo runType paraType paraName blockNo \\\n", "0 run controls2_01 1 main event para06 0 \n", "1 run controls2_01 1 main event para06 1 \n", "2 run controls2_01 1 main event para06 2 \n", "3 run controls2_01 1 main event para06 3 \n", "4 run controls2_01 1 main event para06 4 \n", "5 run controls2_01 1 main event para06 5 \n", "6 run controls2_01 1 main event para06 6 \n", "7 run controls2_01 1 main event para06 7 \n", "8 run controls2_01 1 main event para06 8 \n", "9 run controls2_01 1 main event para06 9 \n", "10 run controls2_01 1 main event para06 10 \n", "11 run controls2_01 1 main event para06 11 \n", "12 run controls2_01 1 main event para06 12 \n", "13 run controls2_01 1 main event para06 13 \n", "14 run controls2_01 1 main event para06 14 \n", "15 run controls2_01 1 main event para06 15 \n", "16 run controls2_01 1 main event para06 16 \n", "17 run controls2_01 1 main event para06 17 \n", "18 run controls2_01 1 main event para06 18 \n", "19 run controls2_01 1 main event para06 19 \n", "20 run controls2_01 1 main event para06 20 \n", "21 run controls2_01 1 main event para06 21 \n", "22 run controls2_01 1 main event para06 22 \n", "23 run controls2_01 1 main event para06 23 \n", "24 run controls2_01 1 main event para06 24 \n", "25 run controls2_01 1 main event para06 25 \n", "26 run controls2_01 1 main event para06 26 \n", "27 run controls2_01 1 main event para06 27 \n", "28 run controls2_01 1 main event para06 28 \n", "29 run controls2_01 1 main event para06 29 \n", "30 run controls2_01 1 main event para06 30 \n", "31 run controls2_01 1 main event para06 31 \n", "32 run controls2_01 1 main event para06 32 \n", "33 run controls2_01 1 main event para06 33 \n", "34 run controls2_01 1 main event para06 34 \n", "35 run controls2_01 1 main event para06 35 \n", "36 run controls2_01 1 main event para06 36 \n", "37 run controls2_01 1 main event para06 37 \n", "38 run controls2_01 1 main event para06 38 \n", "39 run controls2_01 1 main event para06 39 \n", "40 run controls2_01 1 main event para06 40 \n", "41 run controls2_01 1 main event para06 41 \n", "42 run controls2_01 1 main event para06 42 \n", "43 run controls2_01 1 main event para06 43 \n", "44 run controls2_01 1 main event para06 44 \n", "45 run controls2_01 1 main event para06 45 \n", "46 run controls2_01 1 main event para06 46 \n", "47 run controls2_01 1 main event para06 47 \n", "48 run controls2_01 1 main event para06 48 \n", "49 run controls2_01 1 main event para06 49 \n", "50 run controls2_01 1 main event para06 50 \n", "51 run controls2_01 1 main event para06 51 \n", "52 run controls2_01 1 main event para06 52 \n", "53 run controls2_01 1 main event para06 53 \n", "54 run controls2_01 1 main event para06 54 \n", "55 run controls2_01 1 main event para06 55 \n", "56 run controls2_01 1 main event para06 56 \n", "57 run controls2_01 1 main event para06 57 \n", "58 run controls2_01 1 main event para06 58 \n", "59 run controls2_01 1 main event para06 59 \n", " ... ... ... ... ... ... ... \n", "\n", " startBlock trialNo cond onset actualOnset dur context \\\n", "0 1 0 7 0.0 0.004020 0.3 Whole \n", "1 1 1 1 0.3 1.693590 0.3 Parts \n", "2 1 2 1 0.6 3.541586 0.3 Parts \n", "3 1 3 6 0.9 5.205333 0.3 Whole \n", "4 1 4 1 1.2 6.717345 0.3 Parts \n", "5 1 5 1 1.5 8.677413 0.3 Parts \n", "6 1 6 8 1.8 10.013345 0.3 Whole \n", "7 1 7 3 2.1 11.509252 0.3 Parts \n", "8 1 8 7 2.4 13.829205 0.3 Whole \n", "9 1 9 5 2.7 15.749133 0.3 Whole \n", "10 1 10 7 3.0 17.213078 0.3 Whole \n", "11 1 11 8 3.3 18.549068 0.3 Whole \n", "12 1 12 4 3.6 20.044989 0.3 Parts \n", "13 1 13 5 3.9 22.125191 0.3 Whole \n", "14 1 14 3 4.2 23.444928 0.3 Parts \n", "15 1 15 3 4.5 25.412903 0.3 Parts \n", "16 1 16 1 4.8 26.868817 0.3 Parts \n", "17 1 17 4 5.1 28.980800 0.3 Parts \n", "18 1 18 1 5.4 30.556723 0.3 Parts \n", "19 1 19 5 5.7 33.140642 0.3 Whole \n", "20 1 20 4 6.0 34.524658 0.3 Parts \n", "21 1 21 6 6.3 36.140685 0.3 Whole \n", "22 1 22 8 6.6 37.540624 0.3 Whole \n", "23 1 23 5 6.9 39.172514 0.3 Whole \n", "24 1 24 2 7.2 40.596501 0.3 Parts \n", "25 1 25 5 7.5 42.804405 0.3 Whole \n", "26 1 26 6 7.8 44.252363 0.3 Whole \n", "27 1 27 8 8.1 45.700631 0.3 Whole \n", "28 1 28 0 8.4 47.004304 0.3 Fixation \n", "29 1 29 0 8.7 47.304275 0.3 Fixation \n", "30 1 30 0 9.0 47.604785 0.3 Fixation \n", "31 1 31 6 9.3 47.904776 0.3 Whole \n", "32 1 32 5 9.6 49.332309 0.3 Whole \n", "33 1 33 8 9.9 50.620247 0.3 Whole \n", "34 1 34 2 10.2 51.908175 0.3 Parts \n", "35 1 35 3 10.5 53.900168 0.3 Parts \n", "36 1 36 1 10.8 55.388153 0.3 Parts \n", "37 1 37 5 11.1 56.996092 0.3 Whole \n", "38 1 38 3 11.4 58.492151 0.3 Parts \n", "39 1 39 4 11.7 60.523999 0.3 Parts \n", "40 1 40 2 12.0 64.235910 0.3 Parts \n", "41 1 41 1 12.3 66.051824 0.3 Parts \n", "42 1 42 4 12.6 67.811819 0.3 Parts \n", "43 1 43 7 12.9 69.419797 0.3 Whole \n", "44 1 44 7 13.2 70.723707 0.3 Whole \n", "45 1 45 0 13.5 72.083681 0.3 Fixation \n", "46 1 46 0 13.8 72.383803 0.3 Fixation \n", "47 1 47 3 14.1 72.684001 0.3 Parts \n", "48 1 48 0 14.4 75.267621 0.3 Fixation \n", "49 1 49 4 14.7 75.567601 0.3 Parts \n", "50 1 50 8 15.0 79.003531 0.3 Whole \n", "51 1 51 7 15.3 80.659547 0.3 Whole \n", "52 1 52 6 15.6 82.075451 0.3 Whole \n", "53 1 53 7 15.9 83.531444 0.3 Whole \n", "54 1 54 2 16.2 84.843354 0.3 Parts \n", "55 1 55 1 16.5 86.595483 0.3 Parts \n", "56 1 56 1 16.8 88.235367 0.3 Parts \n", "57 1 57 2 17.1 90.387212 0.3 Parts \n", "58 1 58 0 17.4 92.475212 0.3 Fixation \n", "59 1 59 0 17.7 92.775342 0.3 Fixation \n", " ... ... ... ... ... ... ... \n", "\n", " pos corrResp subjResp accuracy RT \n", "0 Bottom Left 3 3 Correct 1691.731086 \n", "1 Top Left 1 4 Incorrect 1847.628840 \n", "2 Top Left 1 1 Correct 1663.642791 \n", "3 Top Right 2 2 Correct 1511.654446 \n", "4 Top Left 1 1 Correct 1959.769867 \n", "5 Top Left 1 1 Correct 1335.666005 \n", "6 Bottom Right 4 4 Correct 1495.693448 \n", "7 Bottom Left 3 3 Correct 2319.651968 \n", "8 Bottom Left 3 3 Correct 1919.638110 \n", "9 Top Left 1 1 Correct 1463.658959 \n", "10 Bottom Left 3 3 Correct 1335.654028 \n", "11 Bottom Right 4 4 Correct 1495.674200 \n", "12 Bottom Right 4 4 Correct 2079.699771 \n", "13 Top Left 1 1 Correct 1319.653252 \n", "14 Bottom Left 3 4 Incorrect 1967.667815 \n", "15 Bottom Left 3 3 Correct 1455.684448 \n", "16 Top Left 1 4 Incorrect 2111.706457 \n", "17 Bottom Right 4 1 Incorrect 1575.592539 \n", "18 Top Left 1 1 Correct 2583.716753 \n", "19 Top Left 1 1 Correct 1383.702553 \n", "20 Bottom Right 4 4 Correct 1615.689649 \n", "21 Top Right 2 2 Correct 1399.671678 \n", "22 Bottom Right 4 4 Correct 1631.644232 \n", "23 Top Left 1 1 Correct 1423.674768 \n", "24 Top Right 2 3 Incorrect 2207.632844 \n", "25 Top Left 1 1 Correct 1447.695395 \n", "26 Top Right 2 2 Correct 1447.696678 \n", "27 Bottom Right 4 4 Correct 1303.450589 \n", "28 NaN NaN NaN Correct NaN \n", "29 NaN NaN NaN Correct NaN \n", "30 NaN NaN NaN Correct NaN \n", "31 Top Right 2 2 Correct 1427.156452 \n", "32 Top Left 1 1 Correct 1287.643999 \n", "33 Bottom Right 4 4 Correct 1287.665385 \n", "34 Top Right 2 2 Correct 1991.666627 \n", "35 Bottom Left 3 3 Correct 1487.696267 \n", "36 Top Left 1 4 Incorrect 1607.679209 \n", "37 Top Left 1 1 Correct 1495.672061 \n", "38 Bottom Left 3 3 Correct 2031.672205 \n", "39 Bottom Right 4 3 Incorrect 3711.614753 \n", "40 Top Right 2 2 Correct 1815.614455 \n", "41 Top Left 1 4 Incorrect 1759.731286 \n", "42 Bottom Right 4 1 Incorrect 1607.676643 \n", "43 Bottom Left 3 3 Correct 1303.664879 \n", "44 Bottom Left 3 3 Correct 1359.686204 \n", "45 NaN NaN NaN Correct NaN \n", "46 NaN NaN NaN Correct NaN \n", "47 Bottom Left 3 3 Correct 2583.301432 \n", "48 NaN NaN NaN Correct NaN \n", "49 Bottom Right 4 3 Incorrect 3435.445341 \n", "50 Bottom Right 4 4 Correct 1655.706348 \n", "51 Bottom Left 3 3 Correct 1415.669888 \n", "52 Top Right 2 2 Correct 1455.673755 \n", "53 Bottom Left 3 3 Correct 1311.654360 \n", "54 Top Right 2 2 Correct 1751.674652 \n", "55 Top Left 1 4 Incorrect 1639.529776 \n", "56 Top Left 1 1 Correct 2151.664557 \n", "57 Top Right 2 2 Correct 2087.715344 \n", "58 NaN NaN NaN Correct NaN \n", "59 NaN NaN NaN Correct NaN \n", " ... ... ... ... ... \n", "\n", "[1920 rows x 19 columns]" ] } ], "prompt_number": 2 }, { "cell_type": "markdown", "metadata": {}, "source": [ "If you did your experiment using `psychopy_ext`, then there is a helper function in the `exp` module, called [`get_behav_data()`](http://psychopy_ext.klab.lt/api/generated/psychopy_ext.exp.get_behav_df.html#psychopy_ext.exp.get_behav_df), that will find and import the relevant data from you experiment." ] }, { "cell_type": "heading", "level": 2, "metadata": {}, "source": [ "Aggegating data" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Typically we want to average data across participants and plot it comparing several conditions. Aggregating data in pandas is not too bad but still might take some effort, and plotting it in a nice way is definitely a not trivial. Let's see how that can be done in `psychopy_ext`. Let's first compute reaction times using the [`stats.aggregate()`](http://psychopy_ext.klab.lt/api/generated/psychopy_ext.stats.aggregate.html#psychopy_ext.stats.aggregate) function:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "from psychopy_ext import stats\n", "rt = stats.aggregate(df, values='RT', cols='context')\n", "rt" ], "language": "python", "metadata": {}, "outputs": [ { "html": [ "
\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
cols.contextWholePartsFixation
RT 1414.651203 2073.345894NaN
\n", "

1 rows \u00d7 3 columns

\n", "
" ], "metadata": {}, "output_type": "pyout", "prompt_number": 3, "text": [ "cols.context Whole Parts Fixation\n", "RT 1414.651203 2073.345894 NaN\n", "\n", "[1 rows x 3 columns]" ] } ], "prompt_number": 3 }, { "cell_type": "markdown", "metadata": {}, "source": [ "If you are used to Excel PivotCharts, this should look familiar. We simply specify the data source (`df`), which column we want to aggregate (`values`) and how it should be structured (`cols`). Here we say that we want to split data by the `context` column. If you look at that column, you'll see there are three unique values in it: 'Whole', 'Parts' and 'Fixation', thus in the output you see an average for each of these values. There were no responses during fixation, so the average is coded as 'NaN' ('not a number')." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Don't want this fixation? Let's filter it out:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "df = df[df.context != 'Fixation']\n", "rt = stats.aggregate(df, values='RT', cols='context')\n", "rt" ], "language": "python", "metadata": {}, "outputs": [ { "html": [ "
\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
cols.contextWholeParts
RT 1414.651203 2073.345894
\n", "

1 rows \u00d7 2 columns

\n", "
" ], "metadata": {}, "output_type": "pyout", "prompt_number": 4, "text": [ "cols.context Whole Parts\n", "RT 1414.651203 2073.345894\n", "\n", "[1 rows x 2 columns]" ] } ], "prompt_number": 4 }, { "cell_type": "markdown", "metadata": {}, "source": [ "The way it works is by first evaluating which elements in the 'context' column are not 'Fixation' (`df.context != 'Fixation'`). The output of this is a boolean vector, whcih we then use to filter the entire DataFrame.\n", "\n", "Now let's compute these averages for each participant separately (this will be used to compute error bars in plotting later):" ] }, { "cell_type": "code", "collapsed": false, "input": [ "rt = stats.aggregate(df, values='RT', cols='context', yerr='subjID')\n", "rt" ], "language": "python", "metadata": {}, "outputs": [ { "html": [ "
\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
cols.contextWholeParts
yerr.subjID
controls2_01 1436.928122 1907.336854
controls2_02 2008.916406 2456.431393
controls2_03 1369.047977 1907.168522
controls2_04 1261.510856 1992.553333
controls2_05 1376.929082 1966.202955
controls2_06 1413.390391 1912.792788
controls2_07 1202.785780 2412.898507
controls2_08 1420.174382 2025.540335
controls2_09 1530.765389 2571.260439
controls2_10 1244.360752 1788.609975
controls2_11 1304.750426 1938.166938
controls2_12 1406.254871 2001.188694
\n", "

12 rows \u00d7 2 columns

\n", "
" ], "metadata": {}, "output_type": "pyout", "prompt_number": 5, "text": [ "cols.context Whole Parts\n", "yerr.subjID \n", "controls2_01 1436.928122 1907.336854\n", "controls2_02 2008.916406 2456.431393\n", "controls2_03 1369.047977 1907.168522\n", "controls2_04 1261.510856 1992.553333\n", "controls2_05 1376.929082 1966.202955\n", "controls2_06 1413.390391 1912.792788\n", "controls2_07 1202.785780 2412.898507\n", "controls2_08 1420.174382 2025.540335\n", "controls2_09 1530.765389 2571.260439\n", "controls2_10 1244.360752 1788.609975\n", "controls2_11 1304.750426 1938.166938\n", "controls2_12 1406.254871 2001.188694\n", "\n", "[12 rows x 2 columns]" ] } ], "prompt_number": 5 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Also for more conditions:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "rt = stats.aggregate(df, values='RT', cols=['pos', 'context'])\n", "rt" ], "language": "python", "metadata": {}, "outputs": [ { "html": [ "
\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
cols.posBottom LeftTop LeftTop RightBottom Right
cols.contextWholePartsWholePartsWholePartsWholeParts
RT 1399.365869 1954.816429 1406.562769 2183.723032 1425.831487 2005.071479 1427.743824 2149.539713
\n", "

1 rows \u00d7 8 columns

\n", "
" ], "metadata": {}, "output_type": "pyout", "prompt_number": 6, "text": [ "cols.pos Bottom Left Top Left Top Right \\\n", "cols.context Whole Parts Whole Parts Whole \n", "RT 1399.365869 1954.816429 1406.562769 2183.723032 1425.831487 \n", "\n", "cols.pos Bottom Right \n", "cols.context Parts Whole Parts \n", "RT 2005.071479 1427.743824 2149.539713 \n", "\n", "[1 rows x 8 columns]" ] } ], "prompt_number": 6 }, { "cell_type": "markdown", "metadata": {}, "source": [ "But what it you want to compute **accuracy**? There's a function for that too, called [`accuracy()`](http://psychopy_ext.klab.lt/api/generated/psychopy_ext.stats.accuracy.html#psychopy_ext.stats.accuracy). For it to work, we need to specify which values are considered \"correct\" and which are considered \"incorrect\":" ] }, { "cell_type": "code", "collapsed": false, "input": [ "acc = stats.accuracy(df, values='accuracy', cols='context', yerr='subjID', correct='Correct', incorrect='Incorrect')\n", "acc" ], "language": "python", "metadata": {}, "outputs": [ { "html": [ "
\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
cols.contextWholeParts
yerr.subjID
controls2_01 0.985507 0.797101
controls2_02 1.000000 0.956522
controls2_03 0.985507 0.898551
controls2_04 0.971014 0.840580
controls2_05 0.971014 0.985507
controls2_06 0.971014 0.985507
controls2_07 0.985507 0.913043
controls2_08 1.000000 0.884058
controls2_09 1.000000 0.811594
controls2_10 0.971014 0.927536
controls2_11 1.000000 0.913043
controls2_12 1.000000 0.971014
\n", "

12 rows \u00d7 2 columns

\n", "
" ], "metadata": {}, "output_type": "pyout", "prompt_number": 7, "text": [ "cols.context Whole Parts\n", "yerr.subjID \n", "controls2_01 0.985507 0.797101\n", "controls2_02 1.000000 0.956522\n", "controls2_03 0.985507 0.898551\n", "controls2_04 0.971014 0.840580\n", "controls2_05 0.971014 0.985507\n", "controls2_06 0.971014 0.985507\n", "controls2_07 0.985507 0.913043\n", "controls2_08 1.000000 0.884058\n", "controls2_09 1.000000 0.811594\n", "controls2_10 0.971014 0.927536\n", "controls2_11 1.000000 0.913043\n", "controls2_12 1.000000 0.971014\n", "\n", "[12 rows x 2 columns]" ] } ], "prompt_number": 7 }, { "cell_type": "heading", "level": 2, "metadata": {}, "source": [ "Plotting" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Because we aggregated data using `psychopy_ext`, plotting it is super quick now with the [`plot()`](http://psychopy_ext.klab.lt/api/generated/psychopy_ext.plot.Plot.plot.html#psychopy_ext.plot.Plot.plot) function:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "%matplotlib inline\n", "from psychopy_ext import plot\n", "plt = plot.Plot()\n", "plt.plot(acc, kind='bar')\n", "plt.show()" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "display_data", "png": "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\nrkJggg==\n", "text": [ "" ] } ], "prompt_number": 8 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Notice how you get error bars for free and even if the two conditions are significantly different from each other!\n", "\n", "It can also produce other kinds of plots (see the [Gallery](http://psychopy_ext.klab.lt/library/plot.html#gallery)). One of the nicer ones is called a [bean plot](http://www.jstatsoft.org/v28/c01/paper). It cleverly combines all data points (as these horizontal bars; if several data points coincide, the line is longer) and the estimated density of the measurements, so that you can quickly see the distribution of your data and spot any outliers or non-normality." ] }, { "cell_type": "code", "collapsed": false, "input": [ "plt = plot.Plot()\n", "plt.plot(acc, kind='bean')\n", "plt.show()" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "display_data", "png": "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\nTkSuQmCC\n", "text": [ "" ] } ], "prompt_number": 9 }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can also easily plot several subplots:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "rt = stats.aggregate(df, values='RT', cols='context', subplots='pos', yerr='subjID')\n", "plt = plot.Plot()\n", "plt.plot(rt, kind='bean')\n", "plt.show()" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "display_data", "png": "iVBORw0KGgoAAAANSUhEUgAAAakAAAEYCAYAAADmugmLAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAIABJREFUeJzt3Xl4VNX9+PH3ZN9DyAKEJYmABxoFWV2rxqqVVmsXrf6w\nFvVRVAQRW6wraFVs3RARtGpVKtpaa63fVutaqpZqFUUQxMMuWwLZ922W3x8zE5KQZWYyc+/cmc/r\neXgecufeySdn7tzPPeeexeZyuRBCCCHCUYzZAQghhBC9kSQlhBAibEmSEkIIEbYkSQkhhAhbkqSE\nEEKELUlSQgghwlac2QGYRSlVCOwANno2xQJNwI1a6//2c+w04Aqt9bWen98GLtZaV4Ugzn8Dy7XW\nr/hxTDrwJpABLAZmAI9rrT8PdnyhopRaBpzq+bEY2Ak0Ay7gRK116wDe+zngS631Q34cEwO8CowD\nHgUGAxu01v8XaBzhIsRlfScwB9jv2WTDfV6+qrX+hWef9cBpWuu6Pt7n3/TyPVBKPUWYnd8Wur48\nB5wJlHs2xQBpwBNa6weUUvnAy1rrk/t5n93Aj3v6DAYaf9QmKY8mrfUk7w9KqQuB54Cj+zmuGBjR\n6eczcX/5QsHl+eePSUCe1nosgFLqIeCJYAcWSlrr+d7/K6V2ATODeBEKpExHAGcDKVprl+ei+VWQ\n4jGVAWX9J6319Z1+xyBgo1LqLa31252/g/28T2/OJDzPb6tcXx7WWj/s3aCUGglsUUq9prXeCvSZ\noDq9T28xDij+aE9S3eUAB7w/KKVmA/MAB3AQmAu0AL8GMpRSz3Q69l9Kqe8DmcBjuO+0XcBDWuvn\nlVKnA/fhvqMsxn1XtRi4HlDAK1rrG/0JVil1HnAbkOB5v18CVcDvgeGeO9T3gHxgtVLq51rrT/35\nHeFIKXUHcDFgB7YCc7XWBzsljim4P8vntdZ39vI2PX5plFInAb8BUgEncCfwPu6aaTzwuefOfQrw\ngFLKrrV+LTh/WfgJUVkPBVJwn6sopZye96gFHgDO8/z/E2C81rrEc9z5SqmbgCHAu8BVwD1Y5/wO\n1+tL989npOe96z01wk1a6zSlVArum4HjgRpgC+DSWl/uOe5qpdQkIA/3+XC7UurZzvFrrff5VlSH\nRfszqWSl1HrPv93AI7gvUCilzgAWAqdrrY8DXgT+prXeC9wBfKi1vkJrfYXnvUqAMuD/gGVa64m4\nm9mWKKVO8OwzFbhbaz0e90l5C/A9YDJwnVJqqK+BK6XGAvcCM7TWk4Grgb8Ce4ErgR1a60la61/i\n/mJcEuZfYJ8opS4HzgGmesp4E+67U69C3Hd+k4CLPF9sX987C3gG+JnWegpwPvA4MAj3Z9nsKdOV\nwDrglxGeoIJR1jbPa+uVUlopVYG7uXS21npdt32vxP1dKAZOBI7icA3KhrsZ6gRgPO7P4ySt9W2E\n7/ltheuLDVjgiXG7UqrcE9e5WutSzz7ez+AOIEZrrXDXjo6j6+fTrLWeBkwHfqGUGt4pgZUEkqBA\nkpT3ojNJa10InA78yXP3cA7uZopKAK31Kty1k0J6r7oeDSRqrf/mOaYUeMXzXi5gl9Z6g2ffHcC/\ntNZ2z++ow3135KuzgGG471DWA6tx35GN6SO+SHAO8IzWutnz86PAd5RS8bjL+ElPmdYBLwPf9eO9\nT8Rdpq95yvR13LWpCfRcppFczhCcsvY2900CjsH9fUjFXTPtzIb7grpKa92mtW4HfsfhMnYBL2mt\nXZ54tuG+Yw9nVri+eJv7JuE+zz/Gfc5/2MO+M3C30qC1rgdWdXufFz2vHcSdJIPy+UR7kupCa/0R\noHHfCdg48mSx0XcTaU/lGdvpmO4PoO0BhNn5d73X6UswCfdd7eYBvKcVxND1c4nBXb7ebY5Or8XS\nexn39IwjBtjSQ5m+7cd7RJJglbUNwJN45gLpwP097NdO1++Qs4fXvfp6BhKWwvj64v18moBLgZOA\nnpoG7Zjw+UiS6kQpdTTuu5XPgbdwN1PkeF67HKjQWm/H/WHFdzrUgfu5kAbalFI/8hyTD/wYeIeB\nfWA9Hfsv4GyllPL8rnOAL4DEHva1e+KLBG8Bl3vax8Hd5v6+1roNdzldopSyeZruLgT+3sv79FSm\n/wPGKqVOBVBKTQC+xl276i6SyrQ3wSjrLuXsSVTXcvj5hZcLd831Z0qpBKVUHHAZXS+EvX2HLPFZ\nhPH1pYPWugb4BbDY8/6dvY77fLB5zomZ+Haj5o0/INGepDq3Ga/H3WRxldZ6u9b6XWAp7ua0Tbjv\nMM71HPdfYJxSytsd9q/Af4CxwA+B+UqpDbhPnru01u979uv+gfp6J/68Uqq+07/7tNZfAbNxNx98\nAdwNnNepaabze/8NeEkpdaaPvy+c/R73Q/NPlFJf4W4Xv8Tzmgt3kv4Ed7PFSq31ml7e595uZfqC\n1roc+Alwv6dMVwOXep4TeN/f6+/Ag0qpS4P614WXYJT1ET0ptdZrgReA5d32fQ73jcJ6YC3umkFT\nt/fqSbie31a5vnT/fF7E/cz1Qbp+fvfh7tjxped3H6Tr59ObvwL/UUp9y8d4urDJUh0iUiil1uAe\nL/Nns2OJdKEoa6XUWbiHTrzg+XkZ7m7ctwTrd4jAKaUuAuq01v9U7nGDfwHe0lr/LpS/N9prUkKI\n8LEZmKWU+sJTu8gGlpgckzhsE3Cbp1b4Je7u7k+H+pdKTUoIIUTYkpqUEEKIsCVJSgghRNiKqGmR\nysvrLdF2mZWVQnW1L51izJWbm+5Tt1Yp9+DytdxByj7YpOzN0Ve5S03KBHFxsWaHEJWk3M0jZW8e\nq5e9JCkhhBBhS5KUEEKIsCVJSgghRNiSJCWEECJsSZISQggRtiRJCSGECFuSpIQQQoQtSVJh4rXX\n/ord7t8aiDt3bmfDhvUhikgIIcwnSSpMrF79HE5n94Uu+7ZmzXvs2rUzRBEJIYT5ImpaJDO1traw\nZMldHDx4kPb2dq6//he89torlJbux+FwctFFl/Cd75zF3LmzmTjxWDZv3kJjYyN33/1b1q37mMrK\nSu688zaWLHmAJ554jI0bv8DpdHLRRTM59dQS5s69issvn82YMWOZP/9aHnjgUf75z3+QkJDAuHHj\nGTcuoPXEhBAirEmSCpK//e0V8vNHcNdd97Fv317ee+9tsrIGs2jR3TQ1NXHFFT9j6tRp2Gw2Jk6c\nyFVXzePJJ1fy7rtv8rOfXcaqVc9w111L+OijtZSWHmDlyqdpbW3lmmsuZ/r0E1i8+F4WLpxPTk4u\nc+cuYOjQoXzve+eRnZ0jCUoIEbEkSQXJ3r17OOGEkwAYMWIkFRUVTJt2PAApKSkUFRWxf/8+AMaP\nHw9AXt4QqqurOt7D5XKxc+d2tP6aefOuBsDhcFBaWsqYMWOZMOE4vvpqE8cff2KXY4QQIlLJM6kg\nKSgoYsuWrwDYv38fa9a809GpoampkR07tjNs2HAAbDbvhL+ujiRjs9lwuZwUFBQxefIUli//HUuX\nrqCk5Ezy84ezadOX7Nq1k4kTJ/PHP64GICYmRpKUECKiSZIKkvPP/zEHDuxn7tzZLFlyFw8++Ch1\ndbXMmXMl8+ZdwxVXzCYrK6vbUbaOhDVx4iQWLryBU045leTkFK677ipmz57Vkbx++9u7ufXWxcyZ\ncz1vvfUGWn+NUuN45ZU/s379Z8b/wUIIYYCIWj7eKuu75OamU15eb3YY/Yq09aQirdxByj7YpOzN\nIetJCSGEsCRJUkIIIcKWJCkhhBBhS5KUEEKIsCVJSgghRNiSwbw9cDgc7N4d3DnxCguPIjY2Nqjv\nKYQQkU6SVA92797JiSdOCep7fvTRZ4wePbbX1+fPn8M111zH+PHFtLe3c+65ZzJr1pXMnHkpAHPn\nzmb79m38/e9vEx8f3+/vmzt3NjfddCujRhUG608QQgjDSXNfmJg2bXrHDBUbNqzn+ONP4uOP1wLQ\n2trKwYNlpKen+zzDhHuQsM9DPoQQIixJkgoT06adwIYNXwDw8cf/5bzzzqehoZ7GxgY2b/6SSZPc\nNbsHH7yPefOuZt68q6mvr8dut/PrX9/BtddewezZl/Hee+90ed+GhgZuv/0mrr/+Gq6//hp27txu\n+N8mhBCBkiQVJsaOPZo9e3YDsGHD5xx33BSmTp3OunWfsH79Zx2Typ533g9Zvvx3DBuWz6ef/o/X\nXnuFrKzBPP74MzzyyEqeeupxamtrPO/q4g9/eIapU6fz6KNPsHDhrTz44G/M+QOFECIA8kwqTMTE\nxDBmzFg+/vi/DB6cTXx8PCeccDJr137A9u3bufDC/8cTTzyGUu4Z1AcPzqa1tYVvvtnN1Kk9z7YO\nsGvXDtavX9dRw6qvrzP+jxNCiABJkgoj06Ydzx/+8Axnn30OABMmHMczzzxJbGwsGRkZQOcZ1N0K\nCorYsGE9p556+hGzrQOMGlXI2WfP4KyzzqG8/BDvvPOmcX+QEEIMkDT3hZGpU49n06aNnHDCKQDE\nxcWRnp7BccdN7vWY88//cR+zrduYNesK/vWvd5k372p+9asbKSgoMuAvEUKI4JBZ0HsQ6nFSVpiV\nGGQWdLPITNzmkbI3R1/lHrLmPqVULPAUcDTgAq4BEoB/AFs9u63UWr+slLoKmA3YgXu01q8rpZKB\n1UAuUA/M0lpXhCrezmJjY/sc0ySs6e677+aaa24wOwwhhB9C+UzqXMCptT5FKXUacC/wd+AhrfXD\n3p2UUkOBecAUIBn4j1LqHeBaYIPW+tdKqYuA2wG5woiANTQ0mB2CEMJPIUtSWuvXlFL/8PxYCNTg\nTkRKKXU+sA130pkOrNVatwPtSqntwATgZOC3nuPfBO4IVaxCCCHCU0g7TmitHUqp54BlwAvAJ8Av\ntdanATuBxUA6UNvpsHogE8gA6rptE0IIEUVC3gVda32ZUmoI8D/gJK31Ac9LrwLLgQ9wJyqvdNy1\nrrpO273b+pSVlUJcnDUmcc3NTe9/J4uwSrnbbLaIKnewTtlDZJ3zIGVvlFB2nLgUGKG1vg9oBpzA\nX5VS87TWnwJnAutw167uVUolAknAeGATsBb4HvApMAN3MutTdXVTUGJ3Op3U1dX2v6MfMjIyiYlx\nV1yt0NsGfD+xg1XuRoikcgfrlH2knfMgZR9MfZV7KGtSfwGeU0q9D8QD84E9wAqlVDtQCszWWjco\npR4FPsTd/Hir1rpVKfU4sEop9SHQCswMYaxd1NXV8uKLzwb1PWfOvJxBg7J6ff3zz9exaNEtFBUd\nhc1mo7W1lbPPPoef/OSift97587t1NfXM3HipGCGLIQQpgtlx4lmoKcr7Ck97Ps08HQPx/80NNGF\nH5vNxtSp07nzznsBaG9vZ+bMn3DOOd8nNTWtz2PXrHmP7OwcSVL96D5bhxAi/Mm0SGHC5XJ1WYaj\nsbGRmJgYtm3byrPPPoXT6aS5uZnFi+8hLi6OX/1qAZmZg5g8eSpvvvk68fHxKDWODz74N1988Rl2\nu4PTTz+DSy6ZZeJfJYQQAyNJKox8/vk65s27mpiYGGJj41iw4CZ27drJHXfcTU5ODs8//yxr1rzL\n2WfPoKqqimeeeYG4uDhcLhfZ2TmMH1/MokW3sHz5k2RnZ/PGG383+08KK1KTEsJ6JEmFkcmTp3LX\nXUu6bPvww3/zyCMPkJKSQnn5ISZMOA6AYcPyiYs7/PF5a2GLFt3N448/SlVVJSeccJJxwQshRAhI\nkgpz99+/hD//+TWSk5O59947cTqdAB09Bb3/d7lctLe3s2bNu9x11xJcLheXXvpTzjzzuwwZMtSs\n8MOK1KSEsB5JUmHCZrP1eBE9++wZXHfdleTk5DJqVCGVlRUd+3spNY4VKx6loKCQjIxMZs++jMTE\nRKZPP0ESVDd2u71LDVQIEd5kFvQeyDgpt0ibBf2RR37DVVfN7be3pNlkJm7zSNmbw5RZ0K0sJiam\nzzFNwrqamprCPkkJIQ6TRQ9F1HC5XNTV9Tu7lhAijEiSElGlsVGW6xDRY8OG9WaHMGCSpERUaW1t\nNTsEIQzz5pvWHyspSUpEFUlSQliLJCkRVVpams0OQQjDRMLYQElSIqpIkhLCWiRJiajS3CxJSkQT\nqUkJYSnNzU1E0gB2IfoSAa19kqREdGlubpJu6CKqOBwOs0MYEElSIqo4HA7Kyw+ZHYYQhmlrazM7\nhAGRJCWiTnV1ldkhCGEIl8tl+WEXkqRE1GlqajQ7BCEMY/XOQpKkRNRpaAjvGaGFCBan00VdXZ3Z\nYQyIJCkRdaTjhIgWLpeLhgZrn++SpETUaWxs6FjhWIjI5qKlpcXsIAZEkpSIOo2NDdTWypIdIjpI\n7z4hLMblclFaut/sMIQwhCQpISyorq7W7BCEMIR0QRfCgqSHn4gWkqSEsCDp4SeihSQpISyooaFO\nJpoVUUGSlBAW1NjYJDNPiKggSUoIC7Lb2zl06KDZYQgRcq2trZZuNZAkJaKWTDQrokFbWxvt7e1m\nhxEwSVIiajU2SnOfiHxtbW00NzeZHUbAJEmJqNXSYt0vrhC+cjqdlj7XJUmJqCUdJ0S0sPIks5Kk\nRNRqbbX2xJtC+MrKk8xKkhJRq6WlxdK9noTwlcNhNzuEgEmSElGrvb2d9nZrT74phC/sdklSQlhO\nW1urpXs9CeErl8u666dJkhJRy+Vy0dTUbHYYQoScwyFJSvjom292y3OQMNLcLD38ROSz2cyOIHCS\npAz2pz89L0uXhxGrLwgnhC+sfF8sScpwLks/xIw08lkIEd7izA4g2rhccvduloULF2Lr1O6xbNky\naXoVEW/+/Pl89NGnZocRMKlJGc5l+anzI4nNyo31QvjIyue51KQM5nS6aGhoIDU10exQos4DDzxA\nYmLXco+Jkfu0UPvii88ZOXII2dnDzQ4lKi1btoyTTjrV7DACJt9Qg7lcLmpra80OQ3jEx8ebHULE\nW7/+MzZt2mR2GFEtJibW7BACJknKYO6xOTKA1AwLFy5k/vz5XbYlJiabFE10kQ4q5rLyzZgkKRNY\nebLHSJOammp2CBHPZrNJkjJZ92ZuK5FnUiZobpZZDszQ/ZlUbGwsSUlJJkYUPRwOh9khRDUrJymp\nSZlAmvvCQ0JCoiQpg8gAdnMlJFj3PJckZQJZtjw8xMfHExsrjQlGkJqUuRISEswOIWCSpExg5VUy\nI0lCgnWbQKxGkpR54uLiiI2V3n3CD/X19bS0yHMps1m5nd5qpOOEeWw2m6XHA1o3cgtrbW2lrOyA\n2WFEvcRE67bTW017e7vZIUQt92wT1p1xQpKUSSoqys0OIepJTco4kqTM456f0rpzVEqSMkltbY3Z\nIUQ9qUkZp729HbtdEpUZnE6nLHoo/CdJynxSkzJOe3s71dVVZocRlRwOB21t1p3UWpKUSWpra6QJ\nxGRJSTIlkhG8y6EcPFhqciTRy8rDXiRJmaS5uYk9e3aZHUZUy8wcZHYIUaW+vs7sEKJWfb11J7WW\nJGWiQ4fKzA4hatlsNtLTM8wOI6rU19ebHULUam627iw3kqQM1nkl2MrKShMjiW7x8QnS3Gewhgap\nSZlFmvtEQGpqKmVOM5PExcVZeqoYK6qvr5eZJ0zS0GDdWqwkKRPV1dVx4MB+s8OISrGxcZZeUttK\nvK0HjY0NVFYeMjma6FRXV9OlFcdKQja7plIqHngGKAASgXuAfcA/gK2e3VZqrV9WSl0FzAbswD1a\n69eVUsnAaiAXqAdmaa0rQhWvWfbt+4YRI0aaHUbUsfJcZlZWWnqAvLxhZocRderqaqmurmTw4Byz\nQ/FbKGtSlwDlWutTgXOAFcBk4CGtdYnn38tKqaHAPOAk4LvAfUqpBOBaYIPn+D8At4cwVtPI2BFz\nxMRILcoMdXXyXMoMDoeDXbt2mB1GQEK5TsHLwF88/48B2oEpgFJKnQ9sA24ApgNrtdbtQLtSajsw\nATgZ+K3n+DeBO0IYq2mqq6twuVzS9CSigpWfjVjdwYPW7E0cspqU1rpRa92glErHnbBuAz4Bfqm1\nPg3YCSwG0oHOnfjrgUwgA6jrti3i1NbWUFUVca2YYc/ptGb7vNU1NkqSMsuhQ2W0tLSYHYbfQrri\nm1JqJPBXYIXW+k9KqUyttTchvQosBz7Anai80oEa3Akqvdu2PmVlpRAXF97PGuLiut4XuFxOKitL\nGTfuKJMiGjgrlPuRnOTmpve/W5izQtknJcXhvTY2NzcxeHBKRDwTtELZx8Yevt40NTWyZ89WTj75\nZBMj8l8oO04MAd4G5mit13g2v6mUul5r/SlwJrAOd+3qXqVUIpAEjAc2AWuB7wGfAjNwJ7M+VVeH\n/4C1niZ63L//IOXl4XeH6etF3Arl3l1raxtlZTVhebH0J3laoewbGlo6/b+BHTv2kZU12MSIehdp\nZd/9evPVV19z9NETTIqmd32Veyg7TtyKu4lukVJqjVJqDe5nUEs9/z8Rd0++g8CjwIfAe8CtWutW\n4HGgWCn1IXAlcFcIYzVMT71A6+pkslmjtbW1ysKThjl80rtcLioqpBu6WcrKDrB3726zw/BLyGpS\nWuv5wPweXjqlh32fBp7utq0Z+GloogsvtbU1OJ1OS6+eaTUOh4OammpSU9PMDiXiOZ0u5s8/fCn4\n3/8+NzGa6OZ0Otm8eSMjRxaaHYrP5KoYBhobG6TzhAkqK2XhSSM4nV1nmZAarLn27PnGUiuDh7Tj\nhOiZ965y2bJlgLsJ5MCB/eTk5JkZVtRpaGgwO4So4HA4Os51gPHjjzExGmG3t7N+/TpmzPiB2aH4\nRGpShuu563NdnXWn0rcqmfDUGN3n62ttte4CfFbT20xIe/bs5ptvrLFUkNSkTND5rtKrsVHu6o1W\nUyMdVozQ3t5OQkJsp5/bTIxGADgcdr74Yh2jRhWG/UQCUpMymPchcucHySBr7ZihtrZGbg4M0NbW\n3u1nqUmFg/3797J58wazw+iXJKkw0dhYL8t2GKy9vY0dO7aZHUbEczi6JympSRmlv5nPv/xyQ9h/\nHtLcZ4KemvuamhqpqqokJyfXhIiiV3n5QbNDiHh2+5HPpGTIhTH6a8mrrq7kk0/+yymnnG5IPIGQ\ns8RgLlfPtSWXy0VpqawtZbRDhw5adp0dq3A47F1+bm1tsfRy5lbiy7m9bdvXVFeH7yrhvSYppVSx\nkYFEi74mNpWZJ0Krpy9sdXUle/bsNj6YKGK32zuew86fPx+HwxHWF8Vo09zcxCeffGR2GL3qqya1\n2rAookrvz52kG7o5du+25jo7VmG324/YVlkpScoIvrYS7N69k71794Q4msDIMymD9dU3orZWalJm\nOHBgnzwjCaH29vYjnsPKulLG8DVJubukf8rIkaNCHJH/+kpSo5RSzwA9PXpzaa2vCFFMEa2vHnx1\ndbXU1taQmTnIwIiiR29f2OrqKrZt0yg13uCIokNPXc6l1cA48+fP77GzVnf79u1l9+6dFBaG17JB\nfSWpBuB93Emq87e7+8/CDy6Xo9fX7HY7u3fvYOLEKQZGFD36uqvctWu7JKkQaWk5MknV1FTLitQG\n8GdxT5fLPfmslZJUldZ6lWGRRAm7ve+xUBUVMtGsGfbv30tdXR0ZGRlmhxJxehqHU1tbTWVlucxX\nGWIul9OnWpTXgQN7qagoD6uhMH01wrcqt/zOG5VSQ5RST4Y4rojVvTtud5WVh6RLdIj0Va6trS18\n+eV6A6OJHj3Neu50OmUgtQH8nSCgvb2dLVu+DFE0gekrSb0FfAZsVUqdpZSKU0rdDGwDCo0ILhJ1\nX7agu6qqSllCIgScTme/yX/Pnt1HTIYqBq65ueelOQ4eLDU4kugTyCw2ZWWlYXWj3FeS+jkwFjgN\nWAC8CVwCXKi1PtuA2CJST91xO3M6nWzfvtWgaKJHc3Nzv88/qqsr2bJlk0ERRYfm5mZaWlp6fO3g\nwVKqqqQreigFctNVVVVBTU11CKIJTF9Jqk5rXaq1/gyYBmwEjtNav2VMaJGnvb3dp5NG7jCDr6Gh\n3qeH9Dt3ShNUMJWWHug1SbW3t7N580aDI4oOSUnxOJ2OLjWplJQUpk2b1u+xDoeD3bt3hjI8v/TV\ncaJzPbEC+IXWOnzqgBZUUVHe6/ounZWXH6ShoZ60tPTQBxUlqqoquf/++33cOzijKw4dkvWqtP66\n16nAAPbu3YXd/m3i4mTIZrAkJcWTnp5EfLyN9HT3NSQlJYVZs2aRl+fuqPLpp5/2+R7htCCor2dG\niySogdu1a2e/Ez6CuzfUli2bmDbtxNAHFSW++SY8R9NHuj17dvdZg62pqWHjxs+ZPHm6gVFFttZW\nO8nJDpKSErnppoWkp6eTmppKbGwshw4dYvPmzf2+R0tL+Myt2FeSKlZKeZduzO/0f3AP5g2vzvQW\nsHPndp9nNSgtPRDiaKLLgQP7uOOOO4iNje1335SUVC6++OckJSUbEFlkq67u/9nG9u2aiROn+PTZ\niP65XC5qapqx2doYPnx4x/bNmzdTUlJCeXloO2YFuwWhryvm0UCJ55/q9P8S4IygRhElKioq6XkC\njyMdOlQqU8cEkT8PgpuaGvnyyy9CGE30qK3tf2aJiopyKe8gc7lcvPDCn7psu+iii0KeoEKh15qU\n1nq3gXFEhZqaKp/3bWtr4+uvNzN16gkhjCh61NRUk5s72Of99+zZzdSpJ8iMCAPk6/RHW7du4dhj\nj5PaVJDYbDYuuOBHXbZt2LCBhoYGXC5XvwN8x40r5owzvhvKEH0mM2oaqLravwlky8qkyS9Yqqp8\nv0EAd+cVWd9r4OrqfGv6qag4xMaNn4c4muhgs9kYNCiZ/PxhHDhwgLq6OhwOB7GxsaSlpfl045Wc\nHD5N3dKlxkC1tTWkpvr+4R86VEZzc3NYnTBWVVVVyYIFK7DZbD5NE+N0Otm27Wvy80cYEF3k8mdx\nw61bv2bChMlSmxqgxMQ44uJi+eabb1ixYgWpqaldevetXbu23/fIyAifSa4lSRmovr6O1NRk5s+f\n32V7bxfNlpYWtm3bwoQJk40IL6IFMmj04MFSmQR1gHobI9WTyspyNm78nEmT+h/LI3rX0tIOwFVX\nXc3o0UVHUOsRAAAdDElEQVSkpqbS1NTEqlWrKC4u7rf7eUJCAkVFo40I1SeSpAxUX1/P0KFD/DpG\nBvYOnMvlorKykqVLl/p1l15ZWUFZ2QGGDRve/86iR97JZbvfmHXW+SbN29NP1vYamJaWdnbv3sXo\n0UUdS3U0NTX1m6AAsrKySUlJNSBK30iSMlBTUyPQe82pJ+4BwHI3H6ikpHgqKiq79O5LSUnx6Y7S\n5XKxZ88uSVID4O+0POXlh9iyZRPFxRNCFFF0cDgc1NcH1hV86NBhQY5mYCRJGai52femD6+amhoq\nKg6Rm+tfDUwcHnlvsw0iISGBBQsWEBMTQ2trq88j7ysrZemUgfDeW/lzY7Zjx1ZJUgNUW1tDY6P/\nN8WxsbGMHn10qMIKiCQpg7hcLuz2I9fV6f84J3v27JYkFQDvyPu0tFTWrFlDTEwMaWlpZGZm+jzy\nvra2RmqyARg82N1c9M9/voHT6SQtLa3XfbtfREtLD3DwYClDhoTXHb2V1NfX9zr7fF/y8oYydGh+\n/zsaSBp+DeJ0OrHbA1sGIpxmJLYS78j78vJyiouLyczM7JgaZtWqVTQ19d/zrK6uNqzmMbMe/5O7\nw2FH669CEEv0aGpq6nfFhZ6MHFkQgmgGRmpSBsnNzWDduk9JSkoiISGhy2v9Vcfr6vwbXyUOc4+8\nf4kbbpjbse0vf/mLTwkK3G37FRUHOybqFL6pqmokKSmea665lq++2sSOHTsAyM3N5cILL2TlypUh\n/f3RPrlva2ur38ekpqZxzDHHhSCagZGalEFsNo7oWWaz2Y5IWD2pr28IaPEy4S7jiy66oMu2Cy64\ngJSUFJ/fo7ZWbhL85X0euHz5MgYPds/0kZuby5o1a1ixYgVz5swxOcLIFsis8iNHFpCUlBSCaAZG\nalIGSEqKJyYmhsbGRu6//wFGjhzRMbguMzOTadOm9fkAv6mpgfr6OjIzw2eAnRV4R97HxaWxefNm\nRowYQVpaGnl5edx44408/PDDPtWoAmk2iXbe54EFBQW88MILvPPOO1xwwQXk5eVx6NAhCgsLufPO\nO/t8jylTjuf44082JuAI4+8EAHFxcRQXTwxRNAMjScoA3i/s0UcfzeLFi5gxYwYvvfQSeXl5bN68\nmcsuu6zfiR8XLVoU8O+P1qYP78j70tJSSkpK0FrT0NBAWloasbGxPnVDh8BWN4123ueBra11jB07\nlrFjxwL49TzQn7kuRVdpaWns3r2btLQ0HnvssX73z88fwZAhQw2IzH+SpAzg/cLW1BxkzJjRbNrk\nXqLcqKnzo5V35P3y5Y9TXl7eMZjX13FSXjKwNDAul4vnn3+Rm2/+Zcc2f54H1tfLKgCBysjIBHwf\n8jJ2rApdMAMkScogLpeLRYsW8eKLL3Rse//997nuuut8On7ixKmcfPKpoQovYrW0tB/RFdfXkfde\nMpdcYGw2G1dcMavLtgsuuMDnmlRzc6N0/w9QcnIy06Ydz4wZ5zB0aN81pJycPMaOHW9QZP6TW0SD\n2Gw2liy5t8s2fx7gt7X5PxBYuCUnJ7Nr1y4WLFjQ5/Q8vR/veycL4eZ9HpiXl8vWrVtZuXIlhw4d\nIi8vj1mzZvl03re1tWO3txsQbWTKyMjwab+CgsKwbi0I38giiPcLW1hYyN69ewP6wra3+z8QWLgN\ndIXd3NzcIEUSPbzPA+vq6rnkkkuYOXMmiYmJOBwO8vLyKC4u7vc9XC4nDof0ag3UoEFZQN/zJiYn\np3DssZOMCikgkqQM4P3C7tu3j7vu+jXl5eWsWrWqI1H58oVtb5c7ykBlZ2dTVFTE0qVL/ZoiBtwP\noLOyckIUWeRqaWmnvr6FXbv2UVPj7sLvcrloaGjgjTfe8Km5NTY2jvj4+FCHGrGysvpf5HP48JFh\nNZlsT+SZlAG8D/CXLn2Uurq6jjubp556yucH+NINOnADGUWfkTFInkkFqKWlnfT0DOLj4/2+OQBI\nSEiUsh+AzMxMoO/JAkaPHmtUOAGTJGWQlpb2I5ou/HmAL4N5AzdixIiOCWZ7egjf15d48ODsUIYW\n8VJT04iLi/N5qY7OZJaPgRk0KIv29t7n78vOzuGoo8I/SUlzn4ESExMB95fS3ztLGasTuJyc3IAv\neOE22abVxMTEEBsb2L1wVpbcIAyEuxt678+khg0bYYmek1KTMlB8fP9TIPXGAudS2EpOTiYlJdXv\nRQ/T0zMs0RwS7mw2W0DNfSNHFgY/mCgyaNAgKirKenzNZrNZ5tyWJGWguLjA29etcMcTzgLpRp6X\nNzTgWoA4zOVydrmb9yVh5eTkUVBQGMKoIl92dg5btzp7LO/s7Bzy80eYEJX/pLnPQC6XK+BjY2Lk\nAfJAJCYm+D1OqrDwqBBGFB3a2toCWqKmsPAouTEboKysLFyunp9l5+UNs0z5ym2igbxfVu+F0r8V\nM+WjGgh/Z4XOyhrMmDHhO1WMVVRVVdLW1urXuT5o0GCOO25qCKOKDvHxCfR2Xzx8uDVqUSBJylAt\nLf6vlOnl7XQhAuN0uvx6JjViRIF0fw6CrVs1drvdr+a+ceOKfVrCRvStt85WqalpFBWNMTiawEmS\nMpB3hddAHiKH4zovVtLW5vuMHYmJiRx7bPgt/mZFmzZt9KtZKT9/BMcdNyWEEUWPqqrKHqc7ys7O\nCWi9KbNYJ9IIMJBl4MN9VHi4a2z0fQn44cNHdkwpIwbmm292A77dmCUmJnH88SeH9TxyVlJWVtpj\nWQ4ebK0ZVCRJGaiiooLs7MAufjk5Mn9coKqrq3xeXddmi2H8+GNDHFH0+PrrLQA+NfeNH38Mw4YN\nNySuaFBefqjHoSvDhllr7J/cshjowIH9AR2XnJzC8OEjgxxN9Ni0aSO1tbU+7TtsWD4FBUUhjig6\nHDxYxpYtm33aNz9/BCeccEqII4ou+/btPWJbSkrKgKYJM4PUpAxSXl7O/v17OfbYY/zu3ZeVNXhA\nA4Gj3aeffuLzvmPHjgthJNHlhRf+QE1NDdnZ2X2e66mpaXz72yXSzBdkO3ZsZ/Dgrs/3MjOziIuz\n1qS9clYY5N133wp4pVFp6huYrVu1T/vl5OQxfvwxIY4merz//hqf9ps4cQrZ2XKOB9Pu3bvYsuWr\nI7Zb8Vmr1KQMsnHjFx3/96d3n81mo7DQOt1Fw43D4WDjxg0+7XvUUWPkbj5I3n33bT77rP/Jk4uK\nxjBx4mQDIoouf/zj8zQ1NR6xPTNzkAnRDIwkKYN8+eXhC2X3WQ/6Slo5OXmWGngXbt588w22b9/a\n734ZGYOYMEEulsHy3HO/77fbf2bmIL797RLLzHxgFS6XizVr/tXja/n51uuYIreNBti5cwebNm0K\n6Njhw0fKl3gAXnvtrz7tV1hYJANIg+Stt97g3/9+r899bDYbEydOIS1NluMIthdffJ4NG9YfsT01\nNY3c3KEmRDQwUpMywB//uLpL1dvX5r7k5GQmTAjvpZ3D2f79+/jww/f73S8pKZmJE2UAaTA4HA6W\nL3+k31rUyJGFFBdPMCiq6GG323n++ed6nCc0PT3dkrOoSE0qxFwuF2vXfhjQsSNHFsid5gA89ND9\nVFZW9LvfiBGjSE/PMCCiyPfIIw/yyScf97mPd9CutBAE34MP3sfnn6/rss37eCEtzZrnuCSpEHv7\n7TdZv/4zv4+Li4vn2GOlFhWoHTu28+abr/e7n80Ww7hxxQZEFPm0/ppVq57pd7+iotHk5uYZEFF0\n2bjxC1av/kOvr1t1pWNJUiH2xz+uDmhV3VGjChkyZFgIIooO99xzJxUV5f3uN2TIUMsNbgxHTqeT\n22//FWVlpX3ul5SUxJQp0w2KKno0Nzdz882/4NChg0e85n28kJFhvZ59IM+kQmrduv91jBXZtWsX\n4B7x7X1A39uzqfj4eCZNkqUKAvXKK3/mnXfe9GnfUaMKpdkpCB588Dc+jYsaObKQzEzrjdUJdzfd\ntIB163rv8m+z2Rg61FrTIXlJTSqEHnvsUb8mNvUqLDxKalEBqqysZOnSB3ya9Tw5OYXi4okGRBXZ\n/ve/j3j22ad82ldm9Ai+5cuX8pe/vNTnPikpqWRlDTYoouAKeU1KKXU88ButdYlSahLwd2Cb5+WV\nWuuXlVJXAbMBO3CP1vp1pVQysBrIBeqBWVrr/p+Ch4l33nmLf/3rnY6fi4rc88FdeOFPKS7+Vq/H\nJSUlM3XqCSGPL1LdcssvfZ5hYujQfJKTk0McUWRrbm5m0aJbqays7Hff7OxcmRcxyF5//f949NGH\n+32kkJqaZtmB6iFNUkqpm4CfAd7qxBTgYa31w532GQrM87yWDPxHKfUOcC2wQWv9a6XURcDtwA2h\njDdYHA4Hy5Y9REtLi9/HHnXUGLKyskMQVeT7/e+f5B//eM3n/UeMGBXCaKLDokW3+twxaMiQodK0\nGkRffrmBRYtu9Wny5NTUNAMiCo1Qp9btwI8B75k5Bfi+Uup9pdTTSqk0YDqwVmvdrrWu8xwzATgZ\n8D5YeBM4M8SxBs2yZQ/12w23J2lp6UybdlIIIop8X321meXLl2K323t83ftM0CslJRWlxhsRWsR6\n9923em1m2rVrV8e/f//73wDk5UkTdrBUVVWxYMFc9u7d49P+qanWXY8upElKa/1X3E14Xv8Dfqm1\nPg3YCSwG0oHOtwL1QCaQAdR12xb2tm/fxnPP/T6gY8eMUZY+mczS1tbGzTf/wq+lUPLyhpCQkBjC\nqCJbS0sL99+/xOdnrnFxcRQWSlNfMDidTubNu9rnOSnB2oumGt2771WttTchvQosBz7Anai80oEa\n3Akqvdu2PmVlpRAXZ96IapfLxeWXL+qzG25MTM/NHYMHD+acc84kMdF6F06zy33evHl8/PF/+9yn\nqKiIO+64o+PnwsJR5OZac9xIZ2aV/S23LOGLL46cesfL+wwWYPTo0cydO5eCAutNydMXs8r+1ltv\n5Z133vJpX+9zqPz8PMue70YnqTeVUtdrrT/F3Xy3DvgEuFcplQgkAeOBTcBa4HvAp8AM3MmsT9XV\nTaGK2yePP/4Yb7zxRp/7OJ1HTlcCUFQ0lrq6NqD/XmlG8fWkNrPc33rrDZ599jm/jomNjWXo0FGU\nlwe2dEqo+XMxMaPsy8pKWbVq1RHbuzepesXFxfH666+HbXl3Fu5lv3bth6xYscLn/Z1OJwBJSZlh\nXf59lbtRScp7Zb4GWKGUagdKgdla6wal1KPAh7ibH2/VWrcqpR4HVimlPgRagZkGxRqQHTu287vf\nrehxzqz+DBqUxaRJ00IQVWRrbGzkvvvu8bub/6BBWdI5ZQCWLn2A0tK+B+12l5SUFKJooofdbue+\n++6mrq6u/507SU1NJSPDEk9LehTyJKW13g2c5Pn/BuCINaK11k8DT3fb1gz8NNTxBYPL5WLRolv6\nfCbSeTDv448/3uW1oqIxMgN3AJYsuYuvvvJ/dvmsrGzpZRaghoYG3n33nR5f69zE19no0aO5++67\nQxlWVFi16hmfO2RVVFTQ2NhIamoqL730EnFxcdhsNhIT42hpaQ9xpMElM04EwTPPPMW7774d0LGp\nqWkyu0QAtm/fxiuvvBzQsVZcnTRcrF79HHv3ftPvfrt27SI9PZ3GxkZiYmL4+GP3xdWqF8pgyssL\n7USvc+bMITs7m/T0dMrKykhJScFmszFoUHLHMzQrlb8kqQGqrq7i6aef6LeZr7fBvCNHFpCUJANK\n/bVixTKqqvofQDpnzhxefvllyssPz+OXnz+cpKR4S31Rgy3UF8r09PQuF8r4+HhLXyit5OWXX2bO\nnDkUFxdTX1/P0KFDO8rdbnfQ2trzMI1wJUmK0H9hO3v55T9TXHwn4O55M378MYb97nBjxB3lihUr\nmDNnDiUlJYC7F+Uxx4wjPt596suFMjTGjx/PmjVrOi6UWVlZlr5QBtOhQ/49UwJYtuxh7r33Tp/2\nLS8vp6SkpKP8i4vds/zb7Q5qapoDem5uJklSJhoyZBjDhllvOedw1rmHWUxMDG1tbRQXF7Nv3z6e\nfPJJZs6cSXx8nFwoA7hQ1tRUM23ahF5nOOjeu69z+XtZ9UJptpqa6j5f76lnZXx8fJef6+paLFnu\nkqQI7AsLsHjxbTz++HKf9vU2O51+eknHtoKCwqhudgq03G+8cR6rVx/ZBbo7p9NJWVkZQ4cOJSEh\ngblz5wJyoQxUWVmZT1PweDmdTsrLyxk+/PCNmFUvlGaz2/27RsTExJCbm9tlW0ZGkiXPe0lSAXK5\nXHz00X/63c/7ADk7O5ulS5dSW1vLypUrSU/P4PTTTyUx0d2rL1oTVSAqKnqfZ7inHmbFxcVs2nS4\nF6BcKAPjdPY9iWn3ss/NzWXNmjVdkpRVL5Rm66+zT+ey95b7qFGjOHDgAOXl5RQXH0NcXCyDBiVb\nrvytOS1uGNi+fRtffbXZp30bGxtpa2sjISGBrKwscnNzueyyWSQmJkR9s1Mgmpt9H0SZm5vLSy91\nnV8uIyNJuqAHICtrMImJvo13+uabb9i3bx/FxcW0t7fz5ZdfYrc7Oi6UUv7+mTbteJ/L7MILL6S4\nuJjNmzfzyCPLiI2NpaamuaP8ExOtVTeRJBWgtWs/9GnNoqKiIgoKChgxYgSbN28mLi6OOXPmMHjw\nYGl2ClBamm+zAnjvKIuLi2lra+Oxxx6joaFRLpQByssb4vOy76mpqSQkJNDW1kZlZSUtLS2WvlCa\n7eSTv82YMWN92nflypVcd911lJSU0NTUREZGBi6Xi5qaZurrWyzXaiNJKkB1db61zXtngv7kk08Y\nNKjr8s3S7BSYvDzfLpSd7yjLyso4dOgQ27btkgtlgGJjY3sdsNtdTk4O1113HSNGjOD8888nJibG\n0hdKs8XGxnLKKaf5vP/KlSspLy8nJiaGtDT3Mh0ul8uS5S5JKkD+LiDW24NMuZv335Qp033ar/Md\nZUFBAQBpaRlyoRyAY4451ud9vRfKzjdiVr1QhoMrr7yawYP9W13XZsOSk1Z3JkkqQGPGHO3TfkVF\nRUyfPp36+noSEhLYu3cvq1evlvb5ATj33B/4vOyD90LplZycIhfKATj//B/7PQ+fzWaTczwIxo49\nmhkzzvV5/127drFo0WJJUtHqtNNKyM/vfYxT50XftmzZ0vFcJCkpCbvdLu3zA5CSksLpp3/Hp307\nfw6AzJE4QJMnT2XqVN9qsp1JkgqOm2++g4KCQj+OcBEba94yOsEgSSpAycnJTJni28zl9fX1VFZW\nUlZWhsvlIj09XdrnB2jOnOvJy/N/fSK5WA7cj350QY/bO98QdP736quvGhxh5BoyZAhXXHGVT/sW\nFRVx112/tvw5L0lqAC6++JJe78yLioq6/MvJyaGgoIC5c+d1rL4rzU6BKyws5Ic//HG/+3X+DERw\nzJx5aUC1KREc11wzlzPOONOnfSOhY5a0Mw3AWWd9l5KS7/DWW//sd9/OS3V8/fXXoQ4tKtx88+38\n5z8fBLRchwhcbGwsl19+JevXf4bDcXiAb283AtOnT+eJJ54wKryIZ7PZuOee33Lhheezf/++Lq91\nnx4pPd2aq/F2JjWpAVq48BZycnL737GDjeRkmfU8GNLS0rjxxpv6fJDfudkpEu4qw8UFF1x0xN28\nNPcZZ8yYscybd4Plnzf5QpLUAE2YcBw///nl/e7nbXK64YYbyMgwbtb1SPeDH/yQH/7wJ2aHEXVs\nNhu33LLIzxs0EUyXX34V3//+eV22dX/MsHjxYpOiCx5JUkGwcOEtlJSc0ec+3rvKZcseITPTuks5\nh6MlSx7g2GMn9PiaPJMKnWOOOZb/9/9+1vFz9wuk99+PfvQjE6OMXDabjd/+dilHH6163ScSGg8k\nSQVBbGwsDzywjKOOGt3vvjabNPcFW1paGosW3X3EjB4i9G6++XamTTve7DCiVnZ2NjfddFtEL5wq\nSSpIRo0qYPHie3ptyvPeVS5ceJOM1QmB004r6bPZtaf1dsTAxcfHc/vtd8kNgol+8IMf8qMf9dzk\nHQnPYSVJBdGMGd/nyiuv6XMfm812xGJkIjhuuWURJ598qtlhRJ0TTzyJiy/+Wf87ipC5557fMG7c\neLPDCAlJUkH2q1/dxjnnfK/X1202W1T0yDFDbGws9977W4YMOXKQrzyTCq3bb7/T58HtIvjS0zOY\nO/cG4uIib1SRJKkgs9lsPPLISsaPL+5lD5flR4CHs299q5hZs64wO4yok5CQwK9+dZvPy6iI4Lvw\nwos57bSuHbikuU/0aPDgwSxe/Osen09FwDkT9hYsWMiJJ55sdhhR5/TTz+C88843O4yoZbPZ+MUv\nFkbcjYIkqRA544yzemynj4Q7m3AXGxvLggULSUlJ6bJdyj70Fi262+cZ6kXwTZ16PCUlhydfjoRG\nG0lSIXT77Xdy3HGTu22VC6URTj/9DM4++5wu26SZNfSys7OZOfNSs8OIapdffmVHD+JIuC+TJBVC\nSUlJXH/9gi7ruUTCSWMVc+cuiIi5y6xmzpzr/VocUQTXKaecGlFj1yRJhdi5557PqaeWmB1GVJow\nYWKXpg9hjISEhF6X8xDGOPnkb5sdQtBIkjLAlVdeLQN4TfKTn/yUmBg5zY121VXXMnr0GLPDiFqX\nXPLziJkjVL69Bigp+Q7HH3+i5ydp7zPSOed8nwkTJpodRtRJSkritNOkBcEsw4blM378t8wOIygk\nSRnEu9y5PJMyls1mkwX6THLhhRdLC4KJjj46MmagkCRlkEsvnSXLGpjkO9/5rtkhRKXJk6dSXHyM\n2WFEraOOOopIaLmRJGWQQYOyOOaYnpeTEKF16qmnMWLESLPDiDo2m41jj5WmVrNMnjwVu91udhgD\nJknKQJE6AWS4i4+PZ8yYsWaHEZWKi6UrulmKi4/B6ZSalPDD+PHfklkPTDJy5EgpexOcddZ3iY2N\nvElPrSAjIzMiBrBLkjLQKaecitPpNDuMqJSfPyIivrBWM3z4CFJTU/rfUYREJMyKLknKQMOHj5Ax\nOyYZMmSo3CCYwGazkZKSanYYUSsSrjfW/wssJCYmJiLubKzoW98qprW11ewwolJystSkzCJJSvhN\nkpQ58vOH43A4zA4jKiUmylgps0TC9UaSlMFkVV5zDBqUJc19JklISOx/JxESN9+82OwQBkySlMEi\nofptRZ1nohfGio+PNzuEqBUJN8VyxTSYJClz2Gw2KXuTREKTkzCPfGsNJt2gzSNlb47hw0eSmZlp\ndhjCoiRJGWz27HlmhxC1zjvvPLNDiEpXXDHbM4+cEP6TJGWwIUOGmB1C1CopkaUjhLAaSVJCCCHC\nliQpIYQQYUuSlBBCiLAlSUoIIUTYkiQlhBAibEmSEkIIEbYkSQkhhAhbkqSEEEKELUlSQgghwpYk\nKSGEEGFLkpQQQoiwJUlKCCFE2JIkJYQQImxJkhJCCBG2JEkJIYQIW5KkhBBChC1JUkIIIcKWJCkh\nhBBhK86MX6qU+hyo9fy4E7gPeA5wApuA67TWLqXUVcBswA7co7V+3YRwhRBCmMTwJKWUSgLQWpd0\n2vZ/wK1a6w+UUo8D5yulPgbmAVOAZOA/Sql3tNZtRscshBDCHGbUpCYCKUqptzy//zZgstb6A8/r\n/wTOBhzAWq11O9CulNoOTADWmRCzEEIIE5jxTKoReEBr/V3gGuCFbq/XA5lABoebBDtvF0IIESXM\nqEltBbYDaK23KaUqgUmdXs8AaoA6IL3T9nSguq83zs1NtwU31NDJzU3vfyeLkHI3j5S9eaTsjWFG\nTepy4CEApVQ+7uTztlLqNM/rM4APgE+AbyulEpVSmcB43J0qhBBCRAmby+Uy9BcqpeKAZ4ECz6ab\ngErgKSAB+Aq4ytO770rcvftigHu11q8aGqwQQghTGZ6khBBCCF/JYF4hhBBhS5KUEEKIsCVJSggh\nRNiSJCWEECJsSZISQggRtiRJCSGECFuSpIQQQoQtSVJCCCHC1v8HTmTAu5pIxC4AAAAASUVORK5C\nYII=\n", "text": [ "" ] } ], "prompt_number": 10 }, { "cell_type": "markdown", "metadata": {}, "source": [ "There are many more option available in this module, so check out its [documentation](http://psychopy_ext.klab.lt/api/index.html#module-psychopy_ext.plot).\n", "\n", "Also, I hope you have noticed by now that the plots in this tutorial are beautiful. They are so pretty by default thanks to a great design by the [Seaborn package](http://web.stanford.edu/~mwaskom/software/seaborn/), so you may want to check out that library too." ] }, { "cell_type": "heading", "level": 1, "metadata": {}, "source": [ "Step 4: Integrated development" ] }, { "cell_type": "heading", "level": 2, "metadata": {}, "source": [ "More tasks" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "So far we've looked at examples where a single experiment is implemented. But often we have several experiments in the same study -- how could we accomodate for this? ``psychopy_ext`` has a concept of a Task: Every experiment of composed of several Tasks that we ask participants to perform. Thus, if you have two tasks, it would looks something like the following (borrowing code from the ``twotasks.py`` demo):" ] }, { "cell_type": "code", "collapsed": false, "input": [ "class TwoTasks(exp.Experiment):\n", " def __init__(self, ...):\n", " self.tasks = [Train, Test]\n", " \n", "class Train(exp.Task):\n", " def __init__(self, parent):\n", " ...\n", " \n", "class Test(exp.Task):\n", " def __init__(self, parent):\n", " ..." ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Here the ``TwoTasks`` class knows about ``Train`` and ``Test`` because we put them in the ``self.tasks`` variable. ``Train`` and ``Test`` know about TwoTasks through the ``parent`` argument that is passed when these classes are initiated during runtime." ] }, { "cell_type": "heading", "level": 2, "metadata": {}, "source": [ "More experiments" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can also have several separate experiments, like Study 1 and Study 2. You simply make two files in the ``scripts`` folder, ``study1.py`` and ``study2.py``. The data for these experiments by default are saved in separate locations (that are called, guess what, ``study1`` and ``study2``)." ] }, { "cell_type": "heading", "level": 2, "metadata": {}, "source": [ "GUI" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "As secretly mentioned before, ``psychopy_ext`` can automatically produce rather complex GUIs such that you can fully customize your experiment before running it. These GUIs are constructed using information you provide at the top of Experiment class from the ``info`` and ``rp`` parameters. And it looks like this:\n", "\n", "![Alt text](images/gui.png)\n", "\n", "Because this GUI is so convenient, it is actually the default mode of running code in ``psychopy_ext``. Calling any command is *unified* within the ``run.py`` file, providing a very easy, replicable way to run code and analyses. It looks something like this:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "%load run.py" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 13 }, { "cell_type": "code", "collapsed": false, "input": [ "#! /usr/bin/env python\n", "from psychopy_ext import ui\n", "\n", "__author__ = \"Jonas Kubilius\"\n", "__version__ = \"0.1\"\n", "exp_choices = [\n", " ui.Choices('scripts.trivial', name='Quick demo'),\n", " ui.Choices('scripts.changedet', name='Change Detection Experiment')\n", " ]\n", "\n", "# bring up the graphic user interface or interpret command line inputs\n", "# usually you can skip the size parameter\n", "ui.Control(exp_choices, title='Demo Project', size=(560,550))\n" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Here we have two important bits: defining Choices (tabs on the left side of GUI) that correspond to diffferent experiments (not tasks), and Control that creates the GUI itself.\n", "\n", "It is not possible to demonstrate this functionality from a notebook directly so we will use IPython magic commands to execute a shell command. Try this:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "%run run.py" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 9 }, { "cell_type": "heading", "level": 2, "metadata": {}, "source": [ "Command line interface" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "But not everybody is keen to use these GUIs. Thus, ``psychopy_ext `` also offers command-line interface in the following manner (running it from the Terminal, Powershell, cmd or a similar program):\n", "\n", "``python run.py myproject exp run --subjid subj_01 --n``\n", "\n", "Here we provide the name of the project (in case there are several), task name in it (experiment, analysis, simulation etc.), function we want to call (``run``) and parameters for ``info`` and ``rp``. Look at [this figure above](#Quick-overview) for a graphical illustration. Notice how you can abbreviate parameters: ``--n`` instead of ``no_output``.\n", "\n", "Try it in practice:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "%run run.py changedet exp run --subjid subj_01 --debug --n" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "heading", "level": 1, "metadata": {}, "source": [ "Extra: Other features that will blow your mind" ] }, { "cell_type": "heading", "level": 2, "metadata": {}, "source": [ "Autorun" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You don't want your experiment to fail with your first participant or after a small tweak in the middle of a pilot run, do you? Imagine you run an experiment for an hour only to learn later that no data was recorded! But then the only way to know if it is really ready is to run it yourself -- which is reasonable to do several times but definitely not after every little tweak that \"shouldn't change anything\". People with a long enough history in development know that these small innocent-looking tweaks can sometimes lead to small accidental issues such as output files not being saved or the script breaking in the middle of running...\n", "\n", "To prevent from such unforseen problems occuring, the best strategy is to have automated tests, called unit tests, that would quickly check if everything is in order. For experiments, this means being able to run the experiment automatically to make sure it works and produces meaningful output. `psychopy_ext` comes with this functionality out of the box. Simply choose the \"unittest\" option in the gui or `--unittest` in the command line.\n", "\n", "Let's try that for the Change Detection experiment:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "%run run.py changedet exp run --d --n --unittest" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "initializing..." ] }, { "output_type": "stream", "stream": "stdout", "text": [ "FreeType import Failed: expected string or Unicode object, NoneType found\n", "\r", " " ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\r" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\n", "Change Detection Experiment\n", "===========================\n", "\n", "In this experiment you will see photographs flickering with a tiny detail in them changing.\n", "Your task is to detect where the change is occuring.\n", "To make it harder, there are bubbles randomly covering the part of the photos.\n", "\n", "Hit **spacebar to begin**. When you detect a change, hit **spacebar** again.\n", "\n", "\r", "trial 1" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "Press spacebar to start the trial.\n", "\n", "Hit spacebar again when you detect a change.\n", "\r", "trial 2" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "Press spacebar to start the trial.\n", "\n", "Hit spacebar again when you detect a change.\n", "\r", "trial 3" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "Press spacebar to start the trial.\n", "\n", "Hit spacebar again when you detect a change.\n", "\r", "trial 4" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "Press spacebar to start the trial.\n", "\n", "Hit spacebar again when you detect a change.\n", "\r", "trial 5" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "Press spacebar to start the trial.\n", "\n", "Hit spacebar again when you detect a change.\n", "\r", "trial 6" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\r", " \r" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "End of Experiment. Thank you!\n", "\n" ] } ], "prompt_number": 1 }, { "cell_type": "markdown", "metadata": {}, "source": [ "You'll notice that the whole experiment runs on its own at a very high pace -- or you may not even see anything really because it's so short. But you see that it prints out what it can see on the screen and thus you can easily verify it went through the entire experiment without any errors.\n", "\n", "So that's cool and good for a quick reassurance that all is in order! But sometimes, especially for longer experiments composed of multiple tasks, you actually want to run the experiment half manually, such that you can read instructions and advance to testing, then quickly go through trials, then read the instruction again etc. For this, there is an `autorun` option that also allows you to choose how quickly we run through trials ('1' means the actualy speed, '100' would be 100x faster).\n", "\n", "Notice that the program is actually performing the experiment just like a participant would, so in the end we get an output file that we can use to meaningfully test our analysis scripts. In fact, it is greatly encouraged to write your data analysis scripts at the same time as your experimental scripts. You will often see that by doing the analysis on such simulated data you will learn that perhaps a particular information about a stimulus or condition is missing and would be useful for the analysis." ] }, { "cell_type": "heading", "level": 2, "metadata": {}, "source": [ "Analyzer" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`psychopy_ext` also has a prototype for a quick data analysis, drawing ideas from Excel's PivotChart, and, consistent with PsychoPy's Builder and Coder modules, named the Analyzer. It is really an early prototype and not even documented yet but here's a quick preview:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "# first get the data from de-Wit, Kubilius et al., (2013) again\n", "import pandas\n", "\n", "path = 'https://bitbucket.org/qbilius/df/raw/aed0ac3eba09d1d688e87816069f5b05e127519e/data/controls2_%02d.csv'\n", "data = [pandas.read_csv(path % i) for i in range(1,13)]\n", "df = pandas.concat(data, ignore_index=True)\n", "df.to_csv('data.csv') # save to a file" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 2 }, { "cell_type": "code", "collapsed": false, "input": [ "# now open the Analyzer GUI\n", "from psychopy_ext import analyzer\n", "analyzer.run()" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "heading", "level": 2, "metadata": {}, "source": [ "Computer vision models included" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Suppose you run an experiment and find that people can tell if there is an animal in an image based on a very brief presentation. Obviously, you may want to make claims that people process this high level object and scene information very quickly, perhaps even in a feedforward manner. But you have to be careful here. Maybe people are able to do this task based on some low level information, such as a particular power spectrum difference between animal and non-animal stimuli.\n", "\n", "A good strategy to address these concerns, to a certain extent at least, is to process your stimuli with a model of an early visual cortex, and use some sort of categorization algorithm (such as computing a distance betweet the two categories, applying a support vector machine, or using a number of other machine learning techniques). Typically it is a tedious procedure but `psychopy_ext` comes with several simple models included in the [`models`](http://psychopy_ext.klab.lt/api/index.html#module-psychopy_ext.models) module, such as Pixelwise (for pixelwise differences), GaborJet (a very simplistic V1 model) from Biederman lab, and HMAX'99, the early implementation of the [HMAX model](http://maxlab.neuro.georgetown.edu/hmax/).\n", "\n", "In the example below, we use the images from the Change Detection experiment to see how different they appear to the HMAX'99 model. You shoudl see that some stimuli are much more different from the others (dark red spots) but on the diagonal images are quite similar to each other, as it should be since version a and version b are only slightly different." ] }, { "cell_type": "code", "collapsed": false, "input": [ "import glob\n", "from scipy import misc\n", "from psychopy_ext import models\n", "import matplotlib.pyplot as plt\n", "\n", "# read images from the Change Detection experiment\n", "ims = glob.glob('../Part2/images/*.jpg')\n", "ims = [misc.imread(im) for im in ims]\n", "# crop and resize them to (128, 128)\n", "ims = [misc.imresize(im[:,im.shape[0]], (128, 128)) for im in ims]\n", "\n", "hmax = models.HMAX()\n", "hmax.compare(ims)" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "heading", "level": 2, "metadata": {}, "source": [ "Export your stimuli to vector graphics" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Often you want to be able to export stimuli that you used in the experiment for using in a paper. One possibility is to capture display using PsychoPy's [`getMovieFrame`](http://www.psychopy.org/api/visual/window.html#psychopy.visual.Window.getMovieFrame) and [`saveMovieFrames`](http://www.psychopy.org/api/visual/window.html#psychopy.visual.Window.saveMovieFrames) fucntionality that captures what is presented on the screen. However, the resolution of this export is going to be low and you will often be unable to use these images on a poster or for a paper.\n", "\n", "A better approach is to export stimuli in the SVG (scalable vector graphics) format that exports objects rather than pixels, and, as the name implies, you can scale them as much as you like without losing quality in programs like [Inkscape](http://www.inkscape.org/), [Scribus](http://www.scribus.net), and [Adobe Illustrator](https://www.adobe.com/en/products/illustrator.html). To help you with that, `psychopy_ext` provides an undocumented (read: **not fully functional**) feature: a whole SVG module in the `exp` class that will try to export your stimuli in the svg format as much as possible. Note that currently it only works with shape and text stimuli (lines, circles, etc) and images (that are, of course, not really scalable). This is how it works:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "from IPython.display import SVG, display\n", "from psychopy import visual, event\n", "from psychopy_ext import exp\n", "\n", "win = visual.Window(size=(400,400))\n", "stim1 = visual.Circle(win)\n", "stim2 = visual.TextStim(win, \"Ceci n'est pas un cercle!\", height=.1)\n", "\n", "# write to svg\n", "svg = exp.SVG(win, filename='stimuli.svg')\n", "svg.write(stim1)\n", "svg.write(stim2)\n", "svg.svgfile.save()\n", "\n", "display(SVG('stimuli.svg'))\n", "\n", "# optional: show stimuli on the screen too\n", "stim1.draw()\n", "stim2.draw()\n", "win.flip()\n", "event.waitKeys()\n", "win.close()" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "FreeType import Failed: expected string or Unicode object, NoneType found\n" ] }, { "metadata": {}, "output_type": "display_data", "svg": [ "Ceci n'est pas un cercle!" ], "text": [ "" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "1.2722 \tWARNING \tCreating new monitor...\n" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "1.2724 \tWARNING \tCreating new monitor...\n" ] } ], "prompt_number": 1 }, { "cell_type": "code", "collapsed": false, "input": [], "language": "python", "metadata": {}, "outputs": [] } ], "metadata": {} } ] }