cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 
Check out the JMP® Marketplace featured Capability Explorer add-in
Choose Language Hide Translation Bar
Steph136
Level I

Extracting peaks from sine wave data

Hi I have some motion data of cyclic movement measured as angle over time. Is there a way to extract the minimum and maximum peak values (I am a noob so a simple explanation is possible please). An example below. I have been doing it manually but I have some data with a large number of peaks so would like a more efficient way. Many Thanks

Steph136_0-1682554488533.png

 

1 ACCEPTED SOLUTION

Accepted Solutions
txnelson
Super User

Re: Extracting peaks from sine wave data

I should have been more descriptive in previous response.

The ability to find the peaks and valleys of a sine wave using JMP is not one of JMP's  built in  analyses.  However, by using JMP's Scripting Language(JSL) a script can be used to get the information you need.

The first thing I needed to start working on a solution to your question was a sample data table.  JMP provides in it's installation however, I am not aware of any of them that provides the data values for a sine wave similar to the sine wave you provided in your initial question.  I therefore had to create my own sample data table.  The first part of my script does nothing more than create a sample data table.

Names Default To Here( 1 );
dt = New Table( "sine", New Column( "x" ), New Column( "y" ) );
For( k = 1, k <= 10, k++,
	yfudgepos = Random Uniform( 0, .2 );
	yfudgeneg = Random Uniform( 0, .2 );
	For( x = 0, x <= 360, x++,
		dt << add rows( 1 );
		y = Sin( x / 180 * Pi() );
		:x[Row()] = x + ((k - 1) * 360);
		If( y < -1 * yfudgeneg,
			:y[Row()] = y + yfudgeneg
		);
		If( y > yfudgepos,
			:y[Row()] = y - yfudgePos
		);
	);
);
dt << select where(ismissing(:y));
dt << delete rows;Th

This part of the script create the sample data table.  It is not a perfect sine wave data table.

I wanted the produced sine wave to have some variability for each wave. To accomplish this, the code adjusts the wave each time it goes above or below the mid  of the wave. .The end result is a wave that has different maximum high points, and different minimum values for each wave.  The end resulting data table has that variability, but it also creates a little step in the wave form.  While the sample data table isn't perfect, it is good enough to use.

 

txnelson_0-1682933354067.png

Here is the part of the script that produces the data for the above wave.

 

Names Default To Here( 1 );
dt = New Table( "sine", New Column( "x" ), New Column( "y" ) );
For( k = 1, k <= 10, k++,
	yfudgepos = Random Uniform( 0, .2 );
	yfudgeneg = Random Uniform( 0, .2 );
	For( x = 0, x <= 360, x++,
		dt << add rows( 1 );
		y = Sin( x / 180 * Pi() );
		:x[Row()] = x + ((k - 1) * 360);
		If( y < -1 * yfudgeneg,
			:y[Row()] = y + yfudgeneg
		);
		If( y > yfudgepos,
			:y[Row()] = y - yfudgePos
		);
	);
);
dt << select where(ismissing(:y));
dt << delete rows;

The table it produces looks like

 

txnelson_1-1682933577226.png

The data table I created has two column.  One called X and one called Y  The columns can be named whatever one would like to call them.  The script just has to be changed to swap out the references to the column names of X and Y to whatever the actual names of the columns are.

Getting back to the actual script that I provided....once the data table is created, I then used Graph Builder to display the data in a graphical form.

 

Graph Builder( Variables( X( :x ), Y( :y ) ), Elements( Points( X, Y, Legend( 3 ) ) ) );

This JSL statement displayed the sine wave graph.

 

The next statement simple pauses the execution of the script, to allow one to see the graph for 5 seconds, before moving on to the actual searching for the minimum and maximum values in the data.

Wait(  5 );

Theremainder of the JSL, is the actual working part of the code, that first finds the maximum and minimum point of each sine wave, and then creates a new data table containing just the 

Top and Bottom data points for each wave.

// Find the min and max for each phase in the sine waves
dt << New Column("T or B",character);

// determine the starting direction of the wave
if(dt:y[1]<dt:y[2], direction = "up", direction = "down");

For( i = 1, i <= N Rows( dt ) - 1, i++,
	If(
		direction == "up" & dt:y[i + 1] < dt:y[i],
		show(i);
			dt:T or B[i] = "Top";
			direction = "down";,
		direction == "down" & dt:y[i + 1] > dt:y[i],
			dt:T or B[i] = "Bottom";
			direction = "up";
	)
);

dt << select where( :T or B != "" );

dtTB = dt << subset( selected columns(0), selected rows(1));

To apply this analysis to your data, you will first need to input your data into a JMP data table in a structure like the sample data table I generated.  Then, assuming your data column were named X and Y, you just need to run the script below, to have it use your new table to analyze the data and find the minimum and maximum values for each wave.

Names Default To Here( 1 );
dt = Current Data Table();

// Find the min and max for each phase in the sine waves
dt << New Column( "T or B", character );

// determine the starting direction of the wave
If( dt:y[1] < dt:y[2],
	direction = "up",
	direction = "down"
);

For( i = 1, i <= N Rows( dt ) - 1, i++,
	If(
		direction == "up" & dt:y[i + 1] < dt:y[i],
			Show( i );
			dt:T or B[i] = "Top";
			direction = "down";,
		direction == "down" & dt:y[i + 1] > dt:y[i],
			dt:T or B[i] = "Bottom";
			direction = "up";
	)
);

dt << select where( :T or B != "" );

dtTB = dt << subset( selected columns( 0 ), selected rows( 1 ) );

 

Jim

View solution in original post

5 REPLIES 5
txnelson
Super User

Re: Extracting peaks from sine wave data

Here is an example that finds the top and bottom peaks for the sine waves.  The script first generates some example data and then goes and finds the top and bottom peaks.

txnelson_0-1682573652002.png

Names Default To Here( 1 );
dt = New Table( "sine", New Column( "x" ), New Column( "y" ) );
For( k = 1, k <= 10, k++,
	yfudgepos = Random Uniform( 0, .2 );
	yfudgeneg = Random Uniform( 0, .2 );
	For( x = 0, x <= 360, x++,
		dt << add rows( 1 );
		y = Sin( x / 180 * Pi() );
		:x[Row()] = x + ((k - 1) * 360);
		If( y < -1 * yfudgeneg,
			:y[Row()] = y + yfudgeneg
		);
		If( y > yfudgepos,
			:y[Row()] = y - yfudgePos
		);
	);
);
dt << select where(ismissing(:y));
dt << delete rows;

Graph Builder( Variables( X( :x ), Y( :y ) ), Elements( Points( X, Y, Legend( 3 ) ) ) );

wait(5);


// Find the min and max for each phase in the sine waves
dt << New Column("T or B",character);

// determine the starting direction of the wave
if(dt:y[1]<dt:y[2], direction = "up", direction = "down");

For( i = 1, i <= N Rows( dt ) - 1, i++,
	If(
		direction == "up" & dt:y[i + 1] < dt:y[i],
		show(i);
			dt:T or B[i] = "Top";
			direction = "down";,
		direction == "down" & dt:y[i + 1] > dt:y[i],
			dt:T or B[i] = "Bottom";
			direction = "up";
	)
);

dt << select where( :T or B != "" );

dtTB = dt << subset( selected columns(0), selected rows(1));

 

Jim
Steph136
Level I

Re: Extracting peaks from sine wave data

Thank you so much for this. Sorry for the silly questions, how do I then apply this to my data. Also could you explain why there is a little step along the 0?

 

Thanks 

 

Steph 

txnelson
Super User

Re: Extracting peaks from sine wave data

I should have been more descriptive in previous response.

The ability to find the peaks and valleys of a sine wave using JMP is not one of JMP's  built in  analyses.  However, by using JMP's Scripting Language(JSL) a script can be used to get the information you need.

The first thing I needed to start working on a solution to your question was a sample data table.  JMP provides in it's installation however, I am not aware of any of them that provides the data values for a sine wave similar to the sine wave you provided in your initial question.  I therefore had to create my own sample data table.  The first part of my script does nothing more than create a sample data table.

Names Default To Here( 1 );
dt = New Table( "sine", New Column( "x" ), New Column( "y" ) );
For( k = 1, k <= 10, k++,
	yfudgepos = Random Uniform( 0, .2 );
	yfudgeneg = Random Uniform( 0, .2 );
	For( x = 0, x <= 360, x++,
		dt << add rows( 1 );
		y = Sin( x / 180 * Pi() );
		:x[Row()] = x + ((k - 1) * 360);
		If( y < -1 * yfudgeneg,
			:y[Row()] = y + yfudgeneg
		);
		If( y > yfudgepos,
			:y[Row()] = y - yfudgePos
		);
	);
);
dt << select where(ismissing(:y));
dt << delete rows;Th

This part of the script create the sample data table.  It is not a perfect sine wave data table.

I wanted the produced sine wave to have some variability for each wave. To accomplish this, the code adjusts the wave each time it goes above or below the mid  of the wave. .The end result is a wave that has different maximum high points, and different minimum values for each wave.  The end resulting data table has that variability, but it also creates a little step in the wave form.  While the sample data table isn't perfect, it is good enough to use.

 

txnelson_0-1682933354067.png

Here is the part of the script that produces the data for the above wave.

 

Names Default To Here( 1 );
dt = New Table( "sine", New Column( "x" ), New Column( "y" ) );
For( k = 1, k <= 10, k++,
	yfudgepos = Random Uniform( 0, .2 );
	yfudgeneg = Random Uniform( 0, .2 );
	For( x = 0, x <= 360, x++,
		dt << add rows( 1 );
		y = Sin( x / 180 * Pi() );
		:x[Row()] = x + ((k - 1) * 360);
		If( y < -1 * yfudgeneg,
			:y[Row()] = y + yfudgeneg
		);
		If( y > yfudgepos,
			:y[Row()] = y - yfudgePos
		);
	);
);
dt << select where(ismissing(:y));
dt << delete rows;

The table it produces looks like

 

txnelson_1-1682933577226.png

The data table I created has two column.  One called X and one called Y  The columns can be named whatever one would like to call them.  The script just has to be changed to swap out the references to the column names of X and Y to whatever the actual names of the columns are.

Getting back to the actual script that I provided....once the data table is created, I then used Graph Builder to display the data in a graphical form.

 

Graph Builder( Variables( X( :x ), Y( :y ) ), Elements( Points( X, Y, Legend( 3 ) ) ) );

This JSL statement displayed the sine wave graph.

 

The next statement simple pauses the execution of the script, to allow one to see the graph for 5 seconds, before moving on to the actual searching for the minimum and maximum values in the data.

Wait(  5 );

Theremainder of the JSL, is the actual working part of the code, that first finds the maximum and minimum point of each sine wave, and then creates a new data table containing just the 

Top and Bottom data points for each wave.

// Find the min and max for each phase in the sine waves
dt << New Column("T or B",character);

// determine the starting direction of the wave
if(dt:y[1]<dt:y[2], direction = "up", direction = "down");

For( i = 1, i <= N Rows( dt ) - 1, i++,
	If(
		direction == "up" & dt:y[i + 1] < dt:y[i],
		show(i);
			dt:T or B[i] = "Top";
			direction = "down";,
		direction == "down" & dt:y[i + 1] > dt:y[i],
			dt:T or B[i] = "Bottom";
			direction = "up";
	)
);

dt << select where( :T or B != "" );

dtTB = dt << subset( selected columns(0), selected rows(1));

To apply this analysis to your data, you will first need to input your data into a JMP data table in a structure like the sample data table I generated.  Then, assuming your data column were named X and Y, you just need to run the script below, to have it use your new table to analyze the data and find the minimum and maximum values for each wave.

Names Default To Here( 1 );
dt = Current Data Table();

// Find the min and max for each phase in the sine waves
dt << New Column( "T or B", character );

// determine the starting direction of the wave
If( dt:y[1] < dt:y[2],
	direction = "up",
	direction = "down"
);

For( i = 1, i <= N Rows( dt ) - 1, i++,
	If(
		direction == "up" & dt:y[i + 1] < dt:y[i],
			Show( i );
			dt:T or B[i] = "Top";
			direction = "down";,
		direction == "down" & dt:y[i + 1] > dt:y[i],
			dt:T or B[i] = "Bottom";
			direction = "up";
	)
);

dt << select where( :T or B != "" );

dtTB = dt << subset( selected columns( 0 ), selected rows( 1 ) );

 

Jim
Steph136
Level I

Re: Extracting peaks from sine wave data

Thank you so much for spending the time to write this detailed reply! I got it to work which is fantastic, thank you again! 

StarfruitBob
Level VI

Re: Extracting peaks from sine wave data

Please click on "Accept as Solution" if you found an answer useful. It highlights to others that this solution has helped you.

Learning every day!