Converting python to JS - NumPy and and random.normal function

Hello all

I’m converting an experiment from the builder to online, and am working through converting individual code snippets from python to JS in the builder. I’m completely new to JS and so any pointers to help me figure this out would be appreciated.

My first question is: Is there an easy way to calculate the mean, median and std in JS?

In the following python code I’ve used NumPy to calculate the mean, median and std of the reaction times that are above 0.1 in the practice block-

import numpy as np

if trialCount>19:
    trials_prac_1.finished = True 

# Select RTs that are above 0.1
allRT = trials_prac_1.data['target_prac_1_resp.rt']
selectRT = allRT > 0.1
RT  = allRT[selectRT]

# calculate mean RT 
meanRT = np.mean(RT)

# calculate median RT
medianRT = np.median(RT)

# define fastest half of the RTs (upper RTs)
upperRT = RT < medianRT 
uRT = RT[upperRT]

# calculate mean and std on upper RTs
uppermeanRT = np.mean(uRT)
upperstdRT = np.std(uRT)

# calculate minimum and maximum duration of the target
minmax = upperstdRT*2

trials_prac_1.addData('upperRT', upperRT)
trials_prac_1.addData('minmax', minmax)

Here’s the JS code I’ve converted so far but am stuck with calculating the mean, median and std-

if (trialCount > 19) {
    trials_prac_1.finished = true;
}

# Select RTs that are above 0.1
allRT = trials_prac_1.data['target_prac_1_resp.rt'];
selectRT = allRT > 0.1;
RT = allRT[selectRT];

# calculate mean RT

# calculate median RT

# define fastest half of the RTs (upper RTs)
upperRT = RT < medianRT;
uRT = RT[upperRT];

# calculate mean and std on upper RTs

# calculate minimum and maximum duration of the target
minmax = upperstdRT*2;

trials_prac_1.addData('upperRT', upperRT);
trials_prac_1.addData('minmax', minmax);

Subsequently in the task, the variables meanRT and minmax are used to calculate the duration of a target using the random.normal function in the following python code:

target_duration = np.random.normal(loc = meanRT, scale = minmax)

And so my second question is: Is there an equivalent of np.random.normal that I can use in JS?

Any pointers would be much appreciated and thanks very much for your time

Rachel

Hi Rachel. Personally I think the easiest way to handle translating any Python code is to make sure you have updated your PsychoPy to PsychoPy 2020. In this version of PsychoPy they introduced a feature that automatically translates your Python code to JS code. Just open up your code component, select code type, and select Auto–>JS. It should automatically translate each line of your Python code to a working JS equivilent.

If updating and using this autotranslate feature isn’t an option, and you need to write manual code, then I recommend using https://www.w3schools.com/js/default.asp as a nice reference on how to do some of the functions you need (mean, median, and std). I’m unsure whether you can use numpy functions in JS, but using that above link I think there is enough information about random number generation in JS to achieve this, but if not I would happily take a few minutes to look into it more for you.

1 Like

Hi Bobby

Thank you for that suggestion - that makes things much easier! I’ve updated the psychopy version to 2020, made sure this is consistent with the platform version online, and used the auto>js to convert the code.

I’ve piloted the experiment online but am getting the following error ‘ReferenceError: Can’t find variable: feedback’. This is the code defining the variable feedback- looking at this I was wondering if you’d be able to advise on whether there’s an error in the js code?

python code:

if cue_prac_1_resp.keys=='space':
    feedback='Too early'
elif target_prac_1_resp.keys=='space':
    if target_prac_1_resp.rt>0.1:
        feedback=''
        trialCount=trialCount+1
elif too_late_prac_1_resp.keys=='space':
    feedback='Try to be faster'
else:
    feedback='No key pressed'

js code:

if ((cue_prac_1_resp.keys === "space")) {
    feedback = "Too early";
} else {
    if ((target_prac_1_resp.keys === "space")) {
        if ((target_prac_1_resp.rt > 0.1)) {
            feedback = "";
            trialCount = (trialCount + 1);
        }
    } else {
        if ((too_late_prac_1_resp.keys === "space")) {
            feedback = "Try to be faster";
        } else {
            feedback = "No key pressed";
        }
    }
}

@rachel.rodrigues it is a little hard to tell just from this code what could be causing the error, what I mean by that is I don’t think it is anything about this code itself, but about how it interacts with the rest of the experiment, so it would be easier to fix if I could see your experiment or get a description of the routines/flow that depends on feedback. Mostly I am wondering whether the response and the feedback are within the same routine, and where in the code component this code exists (beginning of routine, each frame, end of routine, etc.)?
My gut feeling is that it has to do just with the timing/ordering of this code compared to other experiments so my advice for now is to check that the variable feedback always defined before it’s used. If the variable feedback is used in the same routine where it is defined make sure that the code component for it is set to begin routine, and that the code component in the builder window is above any other components that refer to the variable feedback.

Hi Bobby, thanks for your feedback, the code is in the end routine tab (in the routine where a response is made) and the feedback is in a separate routine that comes after the routine that the code is in (sorry I should have mentioned before that it runs fine in the builder, which is why I was thinking it might be an issue with the new js code).

After looking into this I’ve seen that sometimes you may need to declare variables, and I also noticed that the auto js converter has done this for some variables but not for the feedback. I was wondering if this was an issue? I tried the following:

Added this is in above the code above, and then also tried added it to the begin experiment tab but neither worked:

var feedback;

Not sure if I’m completely off track here?

My guess is that there’s an error in the experiment that is preventing PsychoPy from being able to scan the code for variables (which it needs to do to create the necessary var statements). So the absence of var feedback; is indication of an error elsewhere. But there were no errors when you exported the html/js code (ususally on pressing sync)?

Hi Jon, thanks for your reply - I’ve looked into it a bit more and fixed one of the errors by changing an unsupported shape to an image instead. However, I’m also getting this other error:

Which refers to the 5th line of this bit of code:

python-

import numpy as np
if trialCount>19:
    trials_prac_1.finished = True 

    allRT = trials_prac_1.data['target_prac_1_resp.rt']
    selectRT = allRT > 0.1
    RT  = allRT[selectRT]

# calculate mean RT 
    meanRT = np.mean(RT)

# calculate new median RT
    medianRT = np.median(RT)

# define upper RTs 
    upperRT = RT < medianRT 
    uRT = RT[upperRT]

#calculate mean and std on upper RT
    uppermeanRT = np.mean(uRT)
    upperstdRT = np.std(uRT)

#calculate min and max duration
    minmax = upperstdRT*2

    trials_prac_1.addData('RT', RT)
    trials_prac_1.addData('meanRT', meanRT)
    trials_prac_1.addData('medianRT', medianRT)
    trials_prac_1.addData('upperRT', upperRT)
    trials_prac_1.addData('minmax', minmax)

javascript-

import * as np from 'numpy';
var RT, allRT, meanRT, medianRT, minmax, selectRT, uRT, upperRT, uppermeanRT, upperstdRT;
if ((trialCount > 19)) {
    trials_prac_1.finished = true;
    allRT = trials_prac_1.data["target_prac_1_resp.rt"];
    selectRT = (allRT > 0.1);
    RT = allRT[selectRT];
    meanRT = np.mean(RT);
    medianRT = np.median(RT);
    upperRT = (RT < medianRT);
    uRT = RT[upperRT];
    uppermeanRT = np.mean(uRT);
    upperstdRT = np.std(uRT);
    minmax = (upperstdRT * 2);
    trials_prac_1.addData("RT", RT);
    trials_prac_1.addData("meanRT", meanRT);
    trials_prac_1.addData("medianRT", medianRT);
    trials_prac_1.addData("upperRT", upperRT);
    trials_prac_1.addData("minmax", minmax);
}

Does this mean it’s not correctly reading the reaction times from the practice block, and if so could you possibly recommend any way I can alter the code to overcome this?

Hi @Bobby_Thomas, I got thrown off with fixing some other errors and have now come back to calculating the variables in my original post above (meanRT, medianRT, stdRT etc.) I don’t think I’m able to use NumPy in JS so have manually re-written the python code below (by editing bits of code I’ve found online)-

Original python code:

import numpy as np
if trialCount>19:
    trials_prac_1.finished = True 

    allRT = trials_prac_1.data['target_prac_1_resp.rt']
    selectRT = allRT > 0.1
    RT  = allRT[selectRT]

# calculate mean RT 
    meanRT = np.mean(RT)

# calculate new median RT
    medianRT = np.median(RT)

# define upper RTs 
    upperRT = RT < medianRT 
    uRT = RT[upperRT]

#calculate mean and std on upper RT
    uppermeanRT = np.mean(uRT)
    upperstdRT = np.std(uRT)

#calculate min and max duration for target
    minmax = upperstdRT*2

    trials_prac_1.addData('RT', RT)
    trials_prac_1.addData('meanRT', meanRT)
    trials_prac_1.addData('medianRT', medianRT)
    trials_prac_1.addData('upperRT', upperRT)
    trials_prac_1.addData('minmax', minmax)

rewritten JS code:

//terminate practice after 20 correct trials
if ((trialCount > 19)) {
    trials_prac_1.finished = true;
}

//calculate mean RT
dat = psychoJS.experiment._trialsData;
selectRT = dat.filter((trial) => trial['target_prac_1_resp.rt'] > 0.1);
RT = selectRT.map((trial) => trial['target_prac_1_resp.rt']);
meanRT = RT.reduce((a, b) => a + b) / RT.length;

//calculate median RT
function getMedian(RT) {
  RT.sort(function(a, b){return a-b});
  var medianRT;

  if (RT.length % 2 !== 0) {
    medianRT = RT[Math.floor(RT.length / 2)];
  }
  else {
    var mid1 = RT[(RT.length / 2) - 1];
    var mid2 = RT[RT.length / 2];
    medianRT = (mid1 + mid2) / 2;
  }
  return medianRT;
}

//define upper RTs
upperRT = RT.filter((trial) => trial['target_prac_1_resp.rt'] > medianRT);
uRT = upperRT.map((trial) => trial['target_prac_1_resp.rt']);

//calculate mean and std on upper RTs
uppermeanRT = uRT.reduce((a, b) => a + b) / uRT.length;
upperstdRT = math.std(uRT);

// calculate min and max target duration;
minmax = (upperstdRT * 2);

trials_prac_1.addData("RT", RT);
trials_prac_1.addData("meanRT", meanRT);
trials_prac_1.addData("medianRT", medianRT);
trials_prac_1.addData("upperRT", upperRT);
trials_prac_1.addData("minmax", minmax);

The experiment won’t initialise online, so I’m thinking there are errors in this code, which is what I was hoping to check with you? Any comments on how to fix this code would be appreciated. Thank you

Sorry Rachel, I’m really not an expert on JavaScript code in general, my only experience with it has been translating my experiments which don’t involve any of these calculations. https://www.w3schools.com/jsref/jsref_obj_math.asp is the resource I have used in the past, but again I haven’t ever had to define functions or work with that. Hopefully someone else might be able to help translate this code.

Hi Bobby, no worries at all! Thank you for your suggestion about looking at that website - it’s been very useful!

Hi Rachel and Bobby, did you ever succeed in getting the mean/median to work? I need to do the same thing. the only thing i could find were people using sum/length in JS to get mean.

Hi @uqlleow, there was a similar discussion you can find here: Getting the median of an array in javascript

I must add, even though the code does get the median, there is a problem with the stimulus duration that is presented on the screen and the duration that I want (which is calculated as the median of the previous trials). Presented stimulus duration is much higher in some trials. This does not occur when I’m using average so there may be something about median calculation, I don’t know I’m still trying to fix it. But maybe you can get an idea from there.

Thanks pinar! I am using means and have solved my issue using sum/length…hope someone assists with your problem soon though!

1 Like