[solved] Selecting trials within a trial
Hi everyone,
I received great support from this forum the last time and I had a few additional questions about OpenSesame functions.
In my experiment, I'm trying to create one long experimental trial that goes on for 8 minutes. More specifically, I'm trying to create a moving fixation dot that moves from back and forth continuously for 8 mins. Each individual pass from left to right should last for 1 second (therefore, 480 passes in all in one trial)
I managed to program the sinusoidal fixation dot in such a way that it travels for 8 minutes (exp.time() - start_time) > 480000). My questions are:
1. How do I ensure that each pass lasts for 1 second exactly?
2. If I want to present a line stimulus on any one randomly chosen pass (while the fixation dot is still moving back and forth), what would be the best way to do that in my inline script? I did ask for some help and this is what I have so far:
from openexp.keyboard import keyboard
from math import sin
import numpy as np
# Create keyboard reference
kb = keyboard(exp)
cnvs = self.offline_canvas()
# Left and right x coordinate of screen
screen_min_x = 0
screen_max_x = cnvs.xcenter()*2
# Wave properties in pixels
amplitude = 400
wavelength = 50.0
# x to start with
x = cnvs.xcenter()
# horizontal translation in pixels per frame
dx = 3
while True:
# Check for keypress and stop if detected
key, time = kb.get_key(timeout=0)
if (exp.time() - start_time) > 480000:
break
y = cnvs.ycenter()
x = sin((exp.time() - start_time) * dx / 1000.0 ) * amplitude + cnvs.xcenter()
print x
cnvs.clear()
# Draw fixation dot at current position
cnvs.fixdot(x=x,y=y)
# Show the canvas
cnvs.show()
start_time = exp.time()
first_stim_present_times = np.random.randint(2, 5, size = 500).cumsum()
first_stim_present_times = first_stim_present_times[first_stim_present_times < frame_times.max()]
So, I think the last part of the above code picks out random passes (stored in first_stim_present_times ) How do I then draw my stimulus on these chosen passes?
Looking forward to your reply!
Thanks in advance!
Anupama
Comments
Hi Anupama,
Regarding your first question, from a quick glance at your code it seems that you're already guaranteed that each pass lasts exactly a second, because the x-coordinate of the dot is based on a sinus function that uses the current time. It's pretty elegant I must say What more did you hope to achieve?
Second question: before entering the while-loop, you could just pick a random number between 0 and 480, knowing that there are 480 passes in total. The chosen number will be the pass during which you display your line stimulus.
In the while-loop, then, you'll need to keep count of the passes. So right before the while-loop you create a variable
current_pass = 0
, and at the end of every cycle (e.g. belowcnvs.show()
) you insert the linecurrent_pass += 1
.Now you have to add a line of code that draws a line onto your canvas, but this line should be preceded by an if-statement, e.g.
if current_pass == chosen_pass:
.I think that's all you need.
Cheers,
Josh
Hi Josh,
Thank you so much for your reply, that was quite insightful!
I had further queries regarding your advice that I hope you can help me with!
1. I implemented your suggestion in my code and printed out current_pass values. I believe it increments with every increase of the sin function variable (instead of each pass) so I still don't get a count of passes. How can I change this?
2. Secondly, if I do get my counter working, by choosing a random number between 0 and 480, I may not get a stimulus presentation till the very end of the trial whereas I want a number of stimulus presentations in each 8 min trial. Is there a way to accomplish that?
3.Lastly, I also want my line stimulus to be presented in a way that it flashes exactly when the fixation dot passes through the center. With my current sin function, there's no way of knowing if the fixation dot would always cross the canvas x.center(). Can I change my sin function in any way to accomplish that?
Thanks again for your help!
Anupama
Hi,
Woops, you're totally right. Knowing that the dot will have made a cycle every second, your variable that keeps track of the cycles could be
current_pass = int((exp.time()-start_time()) / 1000)
.It may be best to create a list variable that contains the digits 0 to 480, with the command
list_variable = list(range(480))
. If you want, say, 4 stimuli, you can pick 4 random elements from this list:first_pick = list_variable.pop(random.randint(0,480))
. The second pick should pick from a range of 0 to 479, because the list will become shorter with every pick. By 'popping' from the list, you prevent elements from being picked twice.Do you mean you want the line to flash only if the dot is positioned exactly in the center? (It will always pass the center because it will move from left to right; you just don't know if it also appears exactly in the center). In any case, you could make sure that the line is only presented when the dot is at a certain x-value, and this x could be the screen center or a range around the screen center, depending on what you want. (e.g.
if cnvs.xcenter()-20 < x < cnvs.center()+20: cnvs.line(*input arguments here*)
will draw the line if x is in a 40-pixel range around the center).Cheers,
Josh
Hi again Josh!
So the newly defined current_pass does give me an increment according to the passes. (it continually returns the same number till it reaches the next pass for which it increases by 1) But when I run the following code, I get a new kind of error- "IndexError: pop index out of range"
The code below is a bit longer than necessary (since I'm comparing each randomly picked trial to the current_pass) but I couldn't determine what exactly is going wrong.
Do you have any insights?
Thanks again!
Anupama
Hi Anupama,
Could you try changing the lines with the pop commands into
list_variable.pop(random.choice(range(480)))
? I think that should work.Cheers,
Josh
Hi Josh,
Yes, when I implemented that, I didn't get the error anymore. But the line stimulus also started flashing multiple times randomly on the same trial.
I also need to show a second stimulus one second after the first stimulus. I tried implementing the following code but it entered an infinite loop and finally was aborted. I'm sure there's a logical error but I'm not able to detect it. Can you help me with knowing what's going wrong here?
Thanks,
Anupama
#
Hi anapuma,
To me it seems weird that you have
in your while==True loop. This means that these values for all your picks, change with every frame. I doubt that is what you want as this appears quite haphazard to me. Rather, you probably want to determine these picks before you start the loop, but do compare their values to current_pass inside the loop. and act accordingly.
However, maybe I don't really have a clear view of your current problem or what you intend to do. The purpose of your experiment or what you are trying to get on screen is quite unclear to me.
Hi Daniel,
I probably wasn't too clear about what I'm trying to achieve. I am trying to get my first stimulus (a line) to be presented above the sinusoidal fixation dot only when the dot crosses the center. This has to happen on random trials (within the 8 minute trial) but the stimulus should only be presented if the fixation dot travels through the center coordinates of the screen.
For this reason, I was trying to set a counter of individual trials (each sweep from left to right or vice versa) to randomly select any one of these 480 trials and present a stimulus on that trial.
I didn't try implementing the random picks before the start of the while loop, as you suggested but I think that is more feasible.
If I managed to show my first line stimulus correctly, I also need to show a second line stimulus (positioned randomly) exactly one second after the first stimulus presentation (therefore on the next trial)
Hope this was more clear! I'm still a bit stuck on presenting the stimulus over the center on random trials, but if you also have ideas on this, I'd really appreciate if you could help me out!
Thanks,
Anupama
Hi Anapuma,
Your intentions are a bit clearer now; your terminology was a bit confusing. Technically speaking, a trial is just a single instance of your task during which you collect responses, that you (usually) take as one sample in your data set later. Thus a trial in a trial seemed a bit odd; maybe it's better to call it 480 oscillations in a trial?
To determine if your dot is on (or better: close to) the center of the screen, you can compare its x and y coordinates with those of the screen's center. The math module has a very useful function for this that I always use for these kinds of things: math.hypot (or hypotenuse/Euclidian distance). This basically gives you the distance in pixels between two points.
To demonstrate in a simplified version of your script:
Then to your second problem. You want to randomly select some trials on which you want to draw this line. We need to extend the example above by adding an extra criterium when to draw the line. You can use your current_pass variable for that. So if you know there will be 480 oscillations in one trial, you can select 5 of them and show the line during only those instances. The random module has a very useful function for this. If you want to select multiple random samples from a list (and prevent individual items being selected twice) you can use random.sample(). Try this out in python or in the opensesame debug window:
So putting things together, you could do something like this:
I hope this gets you going again for a while. Good luck!
Oh, this works much better! Thanks again Daniel, appreciate the help