Showing posts with label DMDX. Show all posts
Showing posts with label DMDX. Show all posts

29 June 2008

Notepad++: A guide to using regular expressions and extended search mode

The information in this post details how to clean up DMDX .zil files, allowing for easy importing into Excel. However, the explanations following each Find/Replace term will benefit anyone looking to understand how to use Notepad++ extended search mode and regular expressions.

If you are specifically looking for multiline regular expressions, look at this post.

You may already know that I am a big fan of Notepad++. Apparently, a lot of other people are interested in Notepad++ too. My introductory post on Notepad++ is the most popular post on my speechblog. I have a feeling that that is about to change.

Since the release of version 4.9, the Notepad++ Find and Replace commands have been updated. There is now a new Extended search mode that allows you to search for tabs(\t), newline(\r\n), and a character by its value (\o, \x, \b, \d, \t, \n, \r and \\). Unfortunately, the Notepad++ documentation is lacking in its description of these new capabilities. I found Anjesh Tuladhar's excellent slides on regular expressions in Notepad++ useful. After six hours of trial and error, I managed to bend Notepad++ to my will. And so I decided to post what I think is the most detailed step-by-step guide to Search and Replace in Notepad++, and certainly the most detailed guide to cleaning up DMDX .zil output files on the internet.

What's so good about Extended search mode?

One of the major disadvantages of using regular expressions in Notepad++ was that it did not handle the newline character well—especially in Replace. Now, we can use Extended search mode to make up for this shortcoming. Together, Extended and Regular Expression search modes give you the power to search, replace and reorder your text in ways that were not previously possible in Notepad++.

Search modes in the Find/Replace interface

In the Find (Ctrl+F) and Replace (Ctrl+H) dialogs, the three available search modes are specified in the bottom right corner. To use a search mode, click on the radio button before clicking the Find Next or Replace buttons.

Cleaning up a DMDX .zil file

DMDX allows you to run experiments where the user responds by using the mouse or some other input device. Depending on the number of choices/responses (and of course the kind of task), DMDX will output a .zil file containing the results (instead of the traditional .azk file). This is specified in the header along with the various response options available to the participant. For some reason, DMDX outputs the reaction time twice—and on separate lines—in .zil files. Here's a guide for cleaning up these messy .zil files with Notepad++. Explanations of the Notepad++ search terms are provided in bullet points at the end of each step.

Step 1: Backup your original result file (e.g. yourexperiment.zil) and create a copy of that file (yourexperiment_copy.zil) that we will edit and clean up.

Step 2: Open yourexperiment_copy.zil in Notepad++ (version 4.9 or later).



Step 3: Remove all error messages.All lines containing DMDX error messages begin with an exclamation mark. Let's get rid of them.

Bring up the Replace dialog box (Ctrl+H) and select the Regular Expression search mode.

Find what: [!].*

Replace with: (leave this blank)

Press Replace All. All the error messages are gone.


  • [!] finds the exclamation character.

  • .* selects the rest of the line.

Step 4: Get rid of all these blank lines.

Switch to Extended search mode in the Replace dialog.

Find what: \r\n\r\n

Replace with: (leave this blank)

Press Replace All. All the blank lines are gone.



  • \r\n is a newline character (in Windows).

  • \r\n\r\n finds two newline characters (what you get from pressing Enter twice).


Step 5: Put each Item (DMDXspeak for trial) on a new line.

Switch to Regular Expression search mode.

Find what: (\+.*)(Item)

Replace with: \1\r\n\2

Press Replace All. "Item"s have been placed on new lines.



  • \+ finds the + character.

  • .* selects the text after the + up until the word "Item".

  • Item finds the string "Item".

  • () allow us to access whatever is inside the parentheses. The first set of parentheses may be accessed with \1 and the second set with \2.

  • \1\r\n\2 will take + and whatever text comes after it, will then add a new line, and place the string "Item" on the new line.

So far so good. Our aim now is to delete duplicate or redundant information (reaction time data).


Step 6: Remove all newline characters using Extended search mode, replacing them with a unique string of text that we will use as a signpost for redundant data later in RegEx. Choose a string of text that does not appear in you .zil file—I have chosen mork.

Switch to Extended search mode in the Replace dialog.

Find what: \r\n

Replace with: mork

Press Replace All. All the newline characters are gone. Your entire DMDX .zil file is now one very long line of (in my case word-wrapped) text.



Step 7: We're nearly there. Using our mork signpost keyword, let's separate the different RT values.

Stay in Extended search mode.

Find what: ,

Replace with: ,mork

Press Replace All. Now, mork appears after every comma.


Step 8: Let's put the remaining Items on new lines.

Switch to and stay in Regular Expression search mode for the remaining steps.

Find what: mork(Item)

Replace with: \r\n\1

Press Replace All. All "Item"s should now be on new lines.



Step 9: Let's get rid of those duplicate RTs.

Find what: mork ([^A-Za-z]*)mork [^A-Za-z]*\,mork

Replace with: \1,

Press Replace All. Duplicate reaction times are gone. It's starting to look like a result file :)



  • A-Z finds all letters of the alphabet in upper case.

  • a-z finds all lower case letters.

  • A-Za-z will find all alphabetic characters.

  • [^...] is the inverse. So, if we put these three together: [^A-Za-z] finds any character except an alphabetic character.

  • Notice that only one of the [^A-Za-z] is in parentheses (). This is recalled by \1 in the Replace with field. The characters outside of the parentheses are discarded.

Step 10: Let's get rid of all those morks.

Find what: mork

Replace with: (leave blank)

Press Replace All. The morks are gone.



Step 11: Separate each participant's data from the next.

Find what: (\**\*)

Replace with: \r\n\r\n\1\r\n\r\n

Press Replace All. The final product is a beautiful, comma-delimited .zil result file that is ready to be imported into Excel for further analysis.



Notepad++, is there anything it can't do?


Please post your questions in the comments below, rather than emailing me. This way, others can refer to my answers here, saving me many hours of responding to similar emails over and over.

Update 20/2/2009: Having trouble understanding regexp? I have created a new Guide for regular expressions. Check it out.

10 October 2007

Replace No Response trials in DMDX: avoid missing data points

As experimenters, we often want the participant to respond to all trials. A non-response is a missing data point. Missing data points are not good. Therefore, non-responses are not good.

Here is a simple experiment in which participants have to respond to an item by pressing the shift keys:
<ep><fd 1><d 50><vm 800,600,600,8,60><dfs 36><dbc 210210210><cr><rcot><t 3000><id "keyboard"><s 4><g 2><cr><eop>
$
11111 "Beginning of the block" <ms% 1200>;
$
+1 "Item 1" <ms% 1200 > * ;
-2 "Item 2" <ms% 1200 > * ;
-3 "Item 3" <ms% 1200 > * ;
+4 "Item 4" <ms% 1200 > * ;
$
0 "THE END" l;
$

Long response window = no No Response trials = no missing data points
Lidija's solution to this problem was to give her participants a very long response window - for example, 30 seconds. This would be achieved by changing the value of <t N> (currently set for 3 seconds) in the header to 30000 (which is 30 seconds). This almost certainly eliminates the occurrence of No Response trials. However, how would you interpret a 10 second response? Did the participant remember what they heard/saw? Were they guessing? Was it a genuine response? It is very hard to say. You could hope for the best. But, in experiments, things usually go wrong rather than better than you had hoped for.

In order to avoid very long response times, it is often desirable to replace missing trials. This means that participants may only have a small response window, and that No Response trials are re-presented again and again until the participant makes a response.

The question then becomes: when will the No Response trial be re-presented?

Re-present a No Response trial immediately
In DMDX, it is very easy to re-present the last trial if a No Response was recorded. Simply add this code <binr N> to the end of an item before the semi-colon. Here is an example:
e.g. before +1"Item 1"<ms% 1200>*;
e.g. after +1"Item 1"<ms% 1200>*<binr 1>;

This will cause DMDX to loop back to item 1 over and over until the participant responds. You would add <binr 2> to the end of trial 2 and so forth. However, this technique has one major disadvantage: it will continuously present the No Response trial again and again in consecutive trials until the participant responds. The danger is that participants might figure out that they can see/hear the same trial again and again just by not responding. This is also not good.

Re-present a No Response trial at the end of a block of trials
A more useful option might be to re-present No Response trials at the end of a block of trials. Unfortuantely, re-presenting missed trials at the end of a block is very difficult in DMDX. However, I have figured out how to do it :) It is possible through the use of counters (a big thank you goes out to Arman for all of his advice and patience re: counters).

Think of it this way: In an experiment where participants respond by pressing either the left or right shift key, for every + trial, there are three possible outcomes:
left shift = Wrong
right shift = Correct
no response =  No Response

Note: for - trials, left shift would be Correct, right shift would be Wrong.

Create a counter for each response. This means you need (3 * no. of items per block) counters - in my case 3 * 4 = 12 counters. Counters are initialised using the <set cN> keyword. Now, we need keywords that will increment a counter depending on the participant's response.

<incic N> increments n when a Correct response is registered.
<incinr N> increments n when a No Response response is registered.
<inciw N> increments either when a Wrong response is registered, or when a No Response is registered. Note: this is very tricky. I only figured this out by looking in the DMDX help manual. Pay close attention to this sentence:

"In keeping with the precedent set by early branching code wrong responses include no responses."

So, No Response trials increment both <incinr n> and <inciw n> keywords. This is very important.

Lets run through the three possible outcomes and see how they affect the incrementing of the three counters for a trial. I have coloured the Correct counters blue, the Wrong counters red, and the No Response counters yellow.

So, if there's a Correct response in trial 1,
c11=1   c21=0   c31=0.
If the participant makes a Wrong response in trial 2,
c12=0   c22=1   c32=0.
If the participant makes a No Response in trial 3,
c13=0   c23=1   c33=1. This is because a No Response is counted by the <incinr n> and the <inciw n> keywords.

At the end of the block, we need to check if there were any No Response trials, using some fancy math:
999 <branchif 11111, (c11 + c12 + c13 + c14 + c21 + c22 + c23 + c24 - c31 - c32 - c33 - c34 .LT. 4)>;

The .LT. 4 means less than 4 - because I have 4 trials in this example, and want to check whether I have received 4 responses (either Correct or Wrong). If the result of the fancy math above is less than 4 (my number of trials), then it means that there was a No Response trial in this block, and the <branchif -11111 comes into effect, branching back to trial 11111 (at the start of the block).

Now that we have branched back to the beginning of the block, we need to add conditions to each trial to determine if it was a No Response trial on the previous attempt, in order to decide whether it should be re-presented. We insert this code before every trial:
999 <branchif 999, (c11 + c21 - c31 .GT. 0)>;

This line of code tests whether the previous response for the item below it was Correct, Wrong, or a No Response (using a scaled down, 1-item-only version of the fancy math from before). It will only re-present the trial below if the previous response was a No Response. Otherwise, it will skip to the next 999 trial, check what the previous response for the item below that line was and so on. In effect, it is re-presenting No Response trials at the end of the block, and it will continue presenting No Response trials until the participant makes a (Correct or Wrong) response. Note the counters will change for each item.

Any item lines that are not presenting stimuli may be preceded the skip display ~ indicator so they only take a fraction of a millisecond to execute.

Here is the finished code:

<ep><fd 1><d 50><vm 800,600,600,8,60><dfs 36><dbc 210210210><cr><rcot><t 3000><id "keyboard"><s 4><g 2><cr><eop>
$
~100 <set c11=0> <set c12=0> <set c13=0> <set c14=0> <set c21=0> <set c22=0> <set c23=0> <set c24=0> <set c31=0> <set c32=0> <set c33=0> <set c34=0>;
$
$
11111 "Beginning of the block" <ms% 1200>;
$
~999 <branchif 999, (c11 + c21 - c31 .GT. 0)>;
+1 "Item 1" <ms% 1200 > * <incic 11> <inciw 21> <incinr 31>;

~999  <branchif 999, (c12 + c22 - c32 .GT. 0)>;
-2 "Item 2" <ms% 1200 > * <incic 12> <inciw 22> <incinr 32>;

~999  <branchif 999, (c13 + c23 - c33 .GT. 0)>;
-3 "Item 3" <ms% 1200 > * <incic 13> <inciw 23> <incinr 33>;

~999  <branchif 999, (c14 + c24 - c34 .GT. 0)>;
+4 "Item 4" <ms% 1200 > * <incic 14> <inciw 24> <incinr 34>;

$
~999 <branchif 11111, (c11 + c12 + c13 + c14 + c21 + c22 + c23 + c24 - c31 - c32 - c33 - c34 .LT. 4)>;
$

$
0 "THE END" l;
$

Blogged with Flock