Help with Inconsistent Scoring

Hi everyone! I am new to Psychopy (and very, very bad at Python), so I hope that this question has not already been answered or this is the wrong place to ask this. However, thank you so much for your help in advance!

Current Experiment:
I am creating a simple word recall experiment where participants see words presented on the screen and then recall them. I would like the participants to see how many words they correctly recalled after the test (i.e., the number of points they got - one point for each word recalled).

Problem:
I have simplified the experiment so that only a maximum of two words can be recalled, thus the max score would be two points. Unfortunately, I have been having difficulty getting accurate and consistent scores. For example, I would input two of the words correctly and sometimes get two points while other times I would get one point even though the input was exactly the same. Obviously, my code is not working correctly. Below is the code that I am currently using:

Code:
Begin Experiment*:
theseWords=['fish','bat']

End Routine*:

Idx=[Idx_1.text,Idx_2.text]
full_score=0
j=0
i=0
while j<2:
    while i<2:
        if Idx[j] == theseWords[i]:
            full_score+=1
            i+=1
        else:
            i+=1
    i=0
    j+=1

In the above code, theseWords includes the two words that should be correctly recalled. Idx_1 and Idx_2 are the names of the two text boxes in that participants can write their responses (one word per textbox).

Goal:
I would like to have a successful code that checks each response in each textbox to see if a correct word is recalled and then I would like the program to award one point for each correct word and then show participants the correct amount of points they scored.

Attached is hopefully my experiment (if it was actually uploaded) if you would like to view it for more information.

Thank you so, so much for your help! I really appreciate all the help that I can get!

FreeRecallSamp_lastrun.py (20.6 KB)

A couple of important notes:

  1. If someone writes ‘fish’ twice, they’ll get 2 points
  2. The matching you are using is very case- and space-sensitive. In other words, they have to type just the four characters fish and nothing else, no spaces, even a newline character (pressing enter) might make the match fail.
  3. There are some Pythonic shortcuts you can use for your loop structure that will be safer and clearer.

Let’s start with (3). Here’s a rewrite of your exact code but more Pythonic and less likely to get messed up if you add more components or words later:

Idx = [Idx_1.text, Idx_2.text]
full_score = 0
for i in range(0, len(Idx)):
    if Idx[i] in theseWords:
        full_score += 1

The main innovations here are using a “for” loop and using the “in” logical comparator. The “in” statement is one of Python’s really nice features, you can interpret it very literally: If something matches any of the items in an existing list, it will return True.

Now, moving on to problem 2: I suspect this is what’s actually causing the inconsistent scoring. The == operator is very inflexible with strings, even invisible characters can throw it off. A better option might be to use RegEx, which matches a little more flexibly.

Begin experiment code:

import re
theseWords = ['fish','bat']

End routine code:

Idx = [Idx_1.text, Idx_2.text]
full_score = 0
for i in range(0, len(Idx)):
   for j in range(0, len(theseWords)):
        if re.search(theseWords[j], Idx[i], re.IGNORECASE) is not None:
            full_score += 1

It’s a little more complicated, but basically if the string ‘fish’ appears AT ALL in the text box, regardless of upper/lowercase or if there are spaces on either side, then it will score them a point. See if that makes your scoring more consistent. However, it will also mean that words that include that string of letters (e.g., ‘zebrafish’) will score points. There are ways to make more precise regex patterns that eliminate that possibility, I suggest reading up on Python RegEx syntax if you’re curious: Python RegEx

Problem 1 is a little trickier, but I think you can solve it with this:
End routine code:

Idx = [Idx_1.text, Idx_2.text]
full_score = 0
words_found = []
for i in range(0, len(Idx)):
   for j in range(0, len(theseWords)):
        if re.search(theseWords[j], Idx[i], re.IGNORECASE) is not None:
            full_score += 1
            words_found.append(j)
    for k in range(0, len(wordsFound)):
        theseWords.pop(wordsFound[k])

Long story short this removes every word that has already been found from the list ‘theseWords’, so it can’t be found twice. If you need ‘theseWords’ again later in your code, you may want to create a second list for the purpose of scoring, which you would need to do anyway if you want to use more advanced RegEx patterns to find a match.

Hope that gives you some ideas!

2 Likes

Thank you so much, Jonathan! This is incredible and so helpful. I really appreciate your insights.