Repository: psychopy/psychopy

Issue:
no documentation for trialHandler.data.addDataType
The TrialHandler.py demo uses the function addDataType:

```
trials.data.addDataType('choice')  # this will help store things with the stimuli
trials.data.addDataType('RT')  # add as many types as you like
```

But I haven't been able to find any documentation for addDataType in the psychopy.data page (http://www.psychopy.org/api/data.html).  (I'd like to know what it does)


Discussion:
Hi Alex, how's it going :)

Your question is referring to the `DataHandler.addDataType()` method, which [currently](https://github.com/psychopy/psychopy/blob/6ceac363c421f97cff64afbb9bd2cce00c86f73e/psychopy/data/base.py#L499) comes with the following documentation:
```python
    def addDataType(self, names, shape=None):
        """Add a new key to the data dictionary of particular shape if
        specified (otherwise the shape of the trial matrix in the trial
        handler. Data are initialised to be zero everywhere. Not needed
        by user: appropriate types will be added during initialisation
        and as each xtra type is needed.
        """
```
The user would never have to manually invoke this method. In fact, as you call `trials.addData()` with a "new" data type, the `addDataType()` method will be automatically run in the background to create all data structures required to store these data.

The demo should thus be updated; the unnecessary lines should be removed.

On another side note, I would suggest to switch to `TrialHandler2` in future studies – it's our all-new and shiny implementation of a trial handler. There's still a bug in `getFutureTrial()`, but aside from that it's awesome ;) as the data is returned as a pandas `DataFrame`, which is extremely versatile. Maybe we should even switch all demos/examples to use `DataHandler2` instead of `DataHandler`…

----
@peircej, @lindeloev 
What if we added a parameter to `addData()` that would control whether we want it to auto-create missing data types or not? Could serve as a fail-safe, i.e., users could explicitly specify all data types at the beginning of their experimental script, and if they later try to add data with an "unknown" type (e.g., due to a typo), we would raise an exception?
Thanks for the fast help and complete explanation. I did not know about
trialHandler2, cool!  Jon's suggestion seems prudent, otherwise it might
take a user awhile (after running many participants in an important
experimnet) to realize they had a typo that was causing some trials' data
to not be summarized in their summary call, for instance.

On Fri, Sep 29, 2017 at 4:58 PM, Richard Höchenberger <
notifications@github.com> wrote:

> Hi Alex, how's it going :)
>
> Your question is referring to the DataHandler.addDataType() method, which
> currently
> <https://github.com/psychopy/psychopy/blob/6ceac363c421f97cff64afbb9bd2cce00c86f73e/psychopy/data/base.py#L499>
> comes with the following documentation:
>
>     def addDataType(self, names, shape=None):
>         """Add a new key to the data dictionary of particular shape if        specified (otherwise the shape of the trial matrix in the trial        handler. Data are initialised to be zero everywhere. Not needed        by user: appropriate types will be added during initialisation        and as each xtra type is needed.        """
>
> The user would never have to manually invoke this method. In fact, as you
> call trials.addData() with a "new" data type, the addDataType() method
> will be automatically run in the background to create all data structures
> required to store these data.
>
> The demo should thus be updated; the unnecessary lines should be removed.
>
> On another side note, I would suggest to switch to TrialHandler2 in
> future studies – it's our all-new and shiny implementation of a trial
> handler. There's still a bug in getFutureTrial(), but aside from that
> it's awesome ;) as the data is returned as a pandas DataFrame, which is
> extremely versatile.
> ------------------------------
>
> @peircej <https://github.com/peircej>, @lindeloev
> <https://github.com/lindeloev>
> What if we added a parameter to addData() that would control whether we
> want it to auto-create missing data types or not? Could serve as a
> fail-safe, i.e., users could explicitly specify all data types at the
> beginning of their experimental script, and if they later try to add data
> with an unspecified type (e.g., due to a typo), we would raise an error?
>
> —
> You are receiving this because you authored the thread.
> Reply to this email directly, view it on GitHub
> <https://github.com/psychopy/psychopy/issues/1536#issuecomment-333046948>,
> or mute the thread
> <https://github.com/notifications/unsubscribe-auth/AA2FTq2xmbPc8iYXdo73FEhF_YwkeIomks5snJUXgaJpZM4Pn4Rw>
> .
>



-- 
Alex Holcombe | Associate Professor
School of Psychology | Faculty of Science
The University of Sydney
Web <http://www.psych.usyd.edu.au/staff/alexh/lab/>, Twitter
<https://twitter.com/ceptional>, Map
<http://openwetware.org/wiki/Holcombe:Location>

Yes, that method isn't strictly necessary but the idea was that you would define all your types of data at the beginning and when you use them it would be more clear if you'd done it incorrectly. The idea was that using `addData()` on a data type that doesn't already exist would give you a warning (in the same way that you get a warning for trying to use a monitor calibration that doesn't exist). It looks like someone (possibly me a long time ago and I've forgotten) has "simplified" that by removing the code that generated the warning. So there really is no difference now between adding data types in advance and simply allowing them to be created as needed.

To be fair, the warning probably wasn't important anyway - it isn't the sort of error that leaves anyone stranded for long. So let's leave it as is, and update the demo not to mention the non-existent safety feature!

> To be fair, the warning probably wasn't important anyway - it isn't the sort of error that leaves anyone stranded for long. So let's leave it as is, and update the demo not to mention the non-existent safety feature!

How about we

- add an `info` log message if an unknown data type is added
- add a keyword argument to `addData` to disallow (i.e., raise an exception) unknown data types.

The signature of `data.TrialHandler2.addData()` could change from its current form:
```python
    def addData(self, thisType, value):
```
to sth. like:
```python
    def addData(self, thisType, value, addDataType=True):
        ...
        if thisType not in self.columns and not addDataType:
            raise SomeException('Data type %s unknown and auto-creation disabled!' % thisType)
```

Like @alexholcombe stated, this could prevent common mistakes-by-typos by forcing more explicit data specification should a user choose this path. Current behavior would be preserved.
The original intent was indeed to protect against typos. But what I'm saying is that I don't think it's needed. If someone has a typo the worst-case scenario I can imagine is that they end up with two columns (like RT and Rt). That's most likely to be detected during testing and it isn't a dreadful scenario even if it happens. 
I feel like I was being overzealous trying to protect people from it.
Thinking about it, I guess you're right, @peircej. It wouldn't cause "fatal" harm.

Resolving patch:
diff --git a/psychopy/demos/coder/experiment control/TrialHandler.py b/psychopy/demos/coder/experiment control/TrialHandler.py
--- a/psychopy/demos/coder/experiment control/TrialHandler.py	
+++ b/psychopy/demos/coder/experiment control/TrialHandler.py	
@@ -3,6 +3,9 @@
 
 """
 Demo of TrialHandler
+
+The contents of this file are in the public domain.
+
 """
 
 from __future__ import division
@@ -10,6 +13,7 @@
 
 from builtins import range
 from random import random
+
 from psychopy import data
 
 # create your list of stimuli
@@ -19,12 +23,11 @@
 for ori in range(90, 180, 30):
     for sf in [0.5, 1.0, 2.0]:
         # append a python 'dictionary' to the list
-        stimList.append({'sf':sf, 'ori':ori})
+        stimList.append({'sf': sf, 'ori': ori})
 
 # organize them with the trial handler
-trials = data.TrialHandler(stimList, 10, extraInfo= {'participant':"Nobody", 'session':1})
-trials.data.addDataType('choice')  # this will help store things with the stimuli
-trials.data.addDataType('RT')  # add as many types as you like
+trials = data.TrialHandler(stimList, 10,
+                           extraInfo={'participant': "Nobody", 'session': 1})
 
 # run the experiment
 nDone = 0
@@ -39,18 +42,27 @@
     msg = 'trial %i had position %s in the list (sf=%.1f)'
     print(msg % (nDone, trials.thisIndex, thisTrial['sf']))
 
-# after the experiment
+# After the experiment, print a new line
 print('\n')
-trials.printAsText(stimOut=['sf', 'ori'],  # write summary data to screen
-                  dataOut=['RT_mean', 'RT_std', 'choice_raw'])
-trials.saveAsText(fileName='testData',  # also write summary data to a text file
-                  stimOut=['sf', 'ori'],
-                  dataOut=['RT_mean', 'RT_std', 'choice_raw'])
-trials.saveAsExcel(fileName='testData',  # ...or an xlsx file (which supports sheets)
-                  sheetName = 'rawData',
+
+# Write summary data to screen
+trials.printAsText(stimOut=['sf', 'ori'],
+                   dataOut=['RT_mean', 'RT_std', 'choice_raw'])
+
+# Write summary data to a text file ...
+trials.saveAsText(fileName='testData',
                   stimOut=['sf', 'ori'],
                   dataOut=['RT_mean', 'RT_std', 'choice_raw'])
-trials.saveAsPickle(fileName = 'testData')  # this saves a copy of the whole object
-df = trials.saveAsWideText("testDataWide.txt")  # wide is useful for analysis with R or SPSS. Also returns dataframe df
 
-# The contents of this file are in the public domain.
+# ... or an xlsx file (which supports sheets)
+trials.saveAsExcel(fileName='testData',
+                   sheetName='rawData',
+                   stimOut=['sf', 'ori'],
+                   dataOut=['RT_mean', 'RT_std', 'choice_raw'])
+
+# Save a copy of the whole TrialHandler object, which can be reloaded later to
+# re-create the experiment.
+trials.saveAsPickle(fileName='testData')
+
+# Wide format is useful for analysis with R or SPSS.
+df = trials.saveAsWideText('testDataWide.txt')


Test patch: