JMP ワークフローの課題 1: 連続電力データからのモチーフの抽出と識別
JMP ワークフローと JSL ウィザードの呼び出し。 これがあなたのための木曜日の課題です。 添付のデータ セットは、私のランニング パワー メーターからのパワー データのセットです。 私たちがやりたいことは、この投稿に添付されているこの ~1000 ポイントのシグナル データ セット中に発生する 8 つのモチーフを特定して正しくタグ付けし、機能 ID を使用して新しい列を作成し、タグ付けの自動化に使用できるように記述することです。この同じエンベロープに続く将来のデータの。
このデータセットと将来のデータ収集の前提は次のとおりです。
- モチーフの開始前は、パワーは 0 です。
- モチーフの前後にノンゼロになる期間(ウォームアップ、ウォームダウン)があります。 これはテスト対象のシステムの一部ではないため、タグ付けしないでください。
- 各モチーフには、アタック、サステイン、ディケイ、リリースがあります。 機能の形はどのモチーフも同じです。 これは、サンプルの期間にわたって 0 からいくつかのピークに達し、その後、ベースライン ノイズ フロア (ウォーキング パワー) まで下降します。 再びパワーが0になったらモチーフIDは終了です。
- 将来的に特定すべき関数の数は不明です。 データセットには 8 個あります。 ソリューションはそれに対して不変である必要があります。
- 関数の全長とピークはさまざまです。
私たちは左端に取り組んでいます分析ワークフロー今日(下に緑色で表示)。 データはファイル (.csv) から取得され、最終的にこのワークフローを自動化すると、 n .csv ファイル。 データ アクセスが使用されており、今日のタスクは、このサンプル ファイルに対してデータ ブレンディングおよびクリーニング タスクを実行して、将来、ワークフローを他の分析機能に拡張する準備が整ったデータを用意することです。

コメントに解決策を残してください。
スクリプティングRe: JMP ワークフロー 課題 1: 連続電力データからのモチーフの抽出と識別アドインが Mac で動作すると聞いてうれしいです。
そうです、アドインを可能な限り汎用化した後で、「スプリント」のフィルターを追加するのを忘れていました。
次のコード:
- アドインを使用して、Strava から新しいデータを取得します
- 「スプリント」名のみを含むように行をフィルタリングします
- 「2022_03_28 Strava Sprinting Training Data Set 5 Runs」データセットに投稿されたものと一致するように列をフィルタリングします
- モチーフを特定する
- スクリプトをデータ テーブルに追加して、結果をグラフ化します
names default to here(1);
// if add-in is found, get fresh data from Strava, otherwise just use current data table
if( file exists("$ADDIN_HOME(com.caes.ngambles.strava-get-activities)\main.jsl"),
include("$ADDIN_HOME(com.caes.ngambles.strava-get-activities)\main.jsl");
wait(0);
);
// get reference to current data table
dt = current data table();
// Clean up data set for specific motif extraction challenge
if( contains( dt << get column names(string), "name"),
r = dt << get rows where(
!contains(lower case(:name), "sprint")
);
try(dt << delete rows(r));
);
colList = {
"name",
"altitude",
"distance",
"grade_smooth",
"lat",
"lng"
};
for( k = 1, k <= n items(colList), k++,
try(dt << delete column( colList(k) ));
);
// Find Regions that match target shapes
dt << New Column( "Shape Match",
Numeric,
nominal,
Formula(
thresh = 12500;
shapeList = {
{221, 245, 289, 327, 374, 431, 477, 518, 535, 518, 491, 451, 401, 350, 304, 238, 178, 161, 156, 106, 96, 96, 87, 85, 81, 79, 76, 74, 73, 72, 73, 72, 72, 72, 72, 72, 71, 70, 70, 70},
{306, 321, 347, 384, 429, 476, 515, 546, 579, 582, 576, 542, 489, 442, 375, 348, 295, 275, 242, 185, 152, 121, 106, 98, 93, 89, 89, 85, 84, 83, 82, 82, 81, 80, 79, 79, 78, 78, 78, 78},
{357, 390, 401, 428, 475, 505, 558, 604, 619, 634, 634, 625, 581, 530, 483, 444, 378, 357, 324, 282, 214, 186, 162, 153, 137, 132, 124, 109, 104, 97, 94, 90, 90, 89, 88, 87, 87, 85, 85, 86}
};
minErr = Empty();
For( k = 1, k <= N Items( shapeList ), k++,
err = 0;
For( n = 0, n < N Items( shapeList[k] ), n++,
For( m = 1, m <= N Items( shapeList[k] ), m++,
err += (:watts[(Row() + m) - n - 1] - shapeList[k][m]) ^ 2
);
err /= N Items( shapeList[k] ) - 1;
minErr = Minimum( minErr, err );
);
);
If( minErr <= thresh,
flag = 1,
flag = 0
);
flag;
)
);
// Run Region
dt << New Column( "Run Region",
Numeric,
"Continuous",
Formula(
regionCount = Summation( k = 0, 5, Lag( :Shape Match, k ) );
If( regionCount < 6 & region Count > 0 & :Shape Match == 1,
nearStart = 1,
nearStart = 0
);
If( nearStart == 0,
ans = :Shape Match;
,
ans = 0;
If( :watts[row()] > 0 & :watts[row() +1] > 0,
ans = :Shape Match;
)
);
ans;
)
);
// motif ID
dt << New Column( "Motif ID",
Numeric,
Nominal,
Formula(
mID = 0;
For( k = 1, k <= Row(), k++,
If( :Run Region[k - 1] == 0 & :Run Region[k] == 1,
mID++
)
);
If( :Run Region == 1, mID );
)
);
// row state formula column
dt << New Column("Row State Formula",
Row State,
Row State,
Formula(
If(
Is Missing(:Motif ID),
Row State() = Combine States(Hidden State(1), Excluded State(1)),
Row State()
)
)
);
// linear interpolation of zero values within motif ID
dt << New Column( "watts modified",
Numeric,
Continuous,
Formula(
If( Is Missing( :Motif ID ),
Empty(),
If( :watts > 0,
:watts,
rb = Row();
ra = Row();
For( k = Row(), k >= 1, k--,
If( :watts[k] != 0,
rb = k;
Break();
)
);
For( k = Row(), k <= N Rows(), k++,
If( :watts[k] != 0,
ra = k;
Break();
)
);
deltaPower = :watts[rb] - :watts[ra];
deltaTime = rb - ra;
:watts[rb] + (deltaPower / deltaTime) * (Row() - rb);
)
)
)
);
// rank timestamp, Motif ID - x axis values to help graph each motif
dt << New Column( "X",
Numeric,
Continuous,
Formula( Col Rank( :timestamp, :Motif ID ) )
);
// graph results script added to data table
dt << new script(
"Graph Results",
names default to here(1);
dt = current data table();
nw = new window(
"Motif Extraction Results",
h list box(
gb1 = dt << Graph Builder(
Show Control Panel( 0 ),
Show Legend( 0 ),
Show Footer( 0 ),
Variables(
X( :Name( "X" ) ),
Y( :watts modified ),
Wrap( :Motif ID )
),
Elements( Line( X, Y, Legend( 6 ) ), Points( X, Y, Legend( 7 ) ) )
)
,
gb2 = dt << Graph Builder(
Show Control Panel( 0 ),
Show Footer( 0 ),
Variables( X( :X ), Y( :watts modified ), Overlay( :Motif ID ) ),
Elements(
Smoother( X, Y, Legend( 5 ), Lambda( 0.0014 ) ),
Points( X, Y, Legend( 6 ) )
)
)
)
);
);
// run script to make graphs
// eval( dt << get property("Graph Results") );Re: JMP ワークフロー 課題 1: 連続電力データからのモチーフの抽出と識別Mac でも機能しますが、リフレッシュ トークンを間違って入力すると、アドインの読み込みが拒否され、アンインストールして再インストールする必要があります。 これは最近のすべての活動を引っ張っていますか? 関心のあるタイトル (つまり、「スプリント」) を持つものだけを取得するための検索文字列を用意するとよい場合があります。
Re: JMP ワークフロー 課題 1: 連続電力データからのモチーフの抽出と識別Mac でテストできないので、デフォルトで Windows のみを選択しました。WindowsとMacの両方を許可するアドインが添付されています。 動作しない理由はわかりませんが、テストされていません。
Macで正常に動作するかどうか教えてください。
Re: JMP ワークフロー 課題 1: 連続電力データからのモチーフの抽出と識別これが macOS で動作しない理由はありますか? 読み込もうとしたところ、Windows 専用に作成された可能性があります。
Re: JMP ワークフロー 課題 1: 連続電力データからのモチーフの抽出と識別この課題に対する最初のアプローチについて考えていて、もっとうまくやれると思ったので、2 回目の試みを行いました。
今回は、いくつかのターゲット シェイプを定義してから、データを検索して、シェイプが適切に一致する領域を探します。 私の定義したターゲット形状は次のとおりです。

この方法では、必要な調整パラメータが少なくなり、追加のターゲット シェイプを簡単に追加できるため、柔軟性が向上します。
実行中にワット値が低下したときに線形外挿を実行する列 (変更されたワット) を追加しました。
また、モチーフ ID に関連付けられていないデータ ポイントを自動的に非表示にして除外する列 (Row State Formula) も追加しました。
私の意見では、これは課題に対する最初の解決策よりも改善されています。
このメソッドは、「2022_03_28 Strava Sprinting Training Data Set 5 Runs.jmp」データセットから 41 の実行を識別します

Strava データを処理する JSL コードは次のとおりです。
names default to here(1);
<BR />// uncomment the following two lines if you have installed the Strava - Get Activities add-in and want to automatically download the Strava data prior to identifying motifs<BR />// include("$ADDIN_HOME(com.caes.ngambles.strava-get-activities)\main.jsl");<BR />// wait(0);<BR />
dt = current data table();
// Find Regions that match target shapes
dt << New Column( "Shape Match",
Numeric,
nominal,
Formula(
thresh = 12500;
shapeList = {{221, 245, 289, 327, 374, 431, 477, 518, 535, 518, 491, 451, 401, 350, 304, 238, 178, 161, 156, 106, 96, 96, 87, 85, 81, 79, 76,
74, 73, 72, 73, 72, 72, 72, 72, 72, 71, 70, 70, 70}, {306, 321, 347, 384, 429, 476, 515, 546, 579, 582, 576, 542, 489, 442, 375, 348, 295,
275, 242, 185, 152, 121, 106, 98, 93, 89, 89, 85, 84, 83, 82, 82, 81, 80, 79, 79, 78, 78, 78, 78}, {357, 390, 401, 428, 475, 505, 558, 604,
619, 634, 634, 625, 581, 530, 483, 444, 378, 357, 324, 282, 214, 186, 162, 153, 137, 132, 124, 109, 104, 97, 94, 90, 90, 89, 88, 87, 87, 85,
85, 86}};
minErr = Empty();
For( k = 1, k <= N Items( shapeList ), k++,
err = 0;
For( n = 0, n < N Items( shapeList[k] ), n++,
For( m = 1, m <= N Items( shapeList[k] ), m++,
err += (:watts[(Row() + m) - n - 1] - shapeList[k][m]) ^ 2
);
err /= N Items( shapeList[k] ) - 1;
minErr = Minimum( minErr, err );
);
);
If( minErr <= thresh,
flag = 1,
flag = 0
);
)
);
// Run Region - helps clean up regions found by shape match
dt << New Column( "Run Region",
Numeric,
"Continuous",
Formula(
regionCount = Summation( k = 0, 5, Lag( :Shape Match, k ) );
If( regionCount < 6 & region Count > 0 & :Shape Match == 1,
nearStart = 1,
nearStart = 0
);
If( nearStart == 0,
ans = :Shape Match;
,
ans = 0;
If( :watts[row()] > 0 & :watts[row() +1] > 0,
ans = :Shape Match;
)
);
ans;
)
);
// motif ID
dt << New Column( "Motif ID",
Numeric,
Nominal,
Formula(
mID = 0;
For( k = 1, k <= Row(), k++,
If( :Run Region[k - 1] == 0 & :Run Region[k] == 1,
mID++
)
);
If( :Run Region == 1, mID );
)
);
// row state formula column
dt << New Column("Row State Formula",
Row State,
Row State,
Formula(
If(
Is Missing(:Motif ID),
Row State() = Combine States(Hidden State(1), Excluded State(1)),
Row State()
)
)
);
// linear interpolation of zero values within motif ID
dt << New Column( "watts modified",
Numeric,
Continuous,
Formula(
If( Is Missing( :Motif ID ),
Empty(),
If( :watts > 0,
:watts,
rb = Row();
ra = Row();
For( k = Row(), k >= 1, k--,
If( :watts[k] != 0,
rb = k;
Break();
)
);
For( k = Row(), k <= N Rows(), k++,
If( :watts[k] != 0,
ra = k;
Break();
)
);
deltaPower = :watts[rb] - :watts[ra];
deltaTime = rb - ra;
:watts[rb] + (deltaPower / deltaTime) * (Row() - rb);
)
)
)
);
// rank timestamp, Motif ID - x axis values to help graph each motif
dt << New Column( "X",
Numeric,
Continuous,
Formula( Col Rank( :timestamp, :Motif ID ) )
);
Re: JMP ワークフロー 課題 1: 連続電力データからのモチーフの抽出と識別API を通じてワット数が得られると聞いてうれしく思います。
更新されたアクセス コードの自動取得を処理する Strava API コードにさらにいくつかの更新を行い、それを汎用アドインにまとめて添付しました。
アドインを初めて実行すると、Strava API 情報を入力するよう求められます。

これはアドイン フォルダーに保存されるため、再度入力する必要はありません。
アドインは、Strava から利用可能なすべてのアクティビティを含むデータ テーブルを返します。

このアドインは、このプロジェクトだけでなく、Strava からデータを探索したい他のユーザーのために、Strava API からデータを取得するプロセスをさらに合理化する必要があります。
Re: JMP ワークフロー 課題 1: 連続電力データからのモチーフの抽出と識別素晴らしい@ngambles . これはうまく機能し、アクセス トークンの取得方法を説明するビデオへのリンクも投稿していただきありがとうございます。 これらのデータは、私が見たいと思っているとおりに入っています。 さらに、これは、手動でエクスポートして .csv ファイルに結合するよりも優れた改善です。 このデータ プルを自動化し、データをクリーンアップして分析し、定期的に JMP Live に公開するための準備が整いました。 はい、あなたのスクリプトから API を介してワットが入ってきます。

貼り付けました@ブレイディ_ブレイディの式を上から見ても、まだすべての間隔を取得していません。 しかし、おかげで@ngamblesの Strava API コードを使用すると、データセットには、探索して使用する別の興味深い機能があります。これは、平滑化された速度列です。 これは便利だと思います。 また、別の機能として使用する一意のアクティビティ ID も取得しました (任意のアクティビティで 6 ~ 10 のスプリントしか実行しないことを知っています。 MLモデルをもう一度投げるべきだと思います@バイロン_JMPこれで、より大きなトレーニング データ セットと、モデルを構築するためのいくつかの追加機能が得られました。
更新されたトレーニング データ セットを添付しました。

と@ジョーダン_ヒラーあなたも試してみるといいと思います! あなたはデータ クリーンアップのプロです。
Re: JMP ワークフロー 課題 1: 連続電力データからのモチーフの抽出と識別Strava API から直接データを取得することに挑戦しました。距離、速度、位置、高度、および勾配の測定に成功しています。 ただし、電力測定値が得られません。これは、Strava アカウントのレベルが原因であるか、パワー測定機器が不足しているためではないかと疑っています。 私のコードは、Strava データに電力データがあるユーザーに対して機能する可能性があると信じる理由がありますが、確認できません。 この解決策が正しくない場合、必要なものからかけ離れているとは思えません。
私はこれを見つけましたユーチューブビデオStrava API を使い始めるための非常に有益な情報です。 これは、JSL コードが Strava から情報を取得する前に必要な、Strava API からアクセス トークンを取得する方法を明確に示しています。 以下のコードで、 your_access_token_here を、実行前に Strava API から取得したアクセス トークンに置き換えます。
names default to here(1);
accessToken = "your_access_token_here"; // access token from the Strava API
// helpful link to get started with the API: https://www.youtube.com/watch?v=sgscChKfGyg
//////////////////////////////////////////
//////////////////////////////////////////
// get list of activities from Strava
s = New HTTP Request(
URL( "https://www.strava.com/api/v3/athlete/activities?access_token=" || accessToken ),
Method( "GET" ),
Headers( {"Accept: application/json"} )
) << Send;
dtx = json to data table(s);
dtx << set name("Activities List");
// keep only activites marked as sprint
r = dtx << get rows where(
!contains(lowercase(:name), "sprint")
);
dtx << delete rows(r);
if( n rows(dtx) == 0,
close(dtx, no save);
throw();
);
// get list of unique activity ids
summarize( activityList = by(dtx:id));
// get and combine the data from each activity
dty = new table();
dty << set name("Strava Data_temp");
for( k = 1, k <= n items(activityList), k++,
actStream = New HTTP Request(
URL( "https://www.strava.com/api/v3/activities/" || activityList[k] || "/streams?keys=latlng,watts,distance,velocity,altitude,velocity_smooth,cadence,temp,moving,grade_smooth&access_token=" || accessToken ),
Method( "GET" ),
Headers( {"Accept: application/json"} )
) << Send;
dtz = json to data table(actStream);
dtz << new column("Activity ID", numeric, nominal, set each value(num(activityList[k])));
dty << concatenate(dtz, append to first table);
close(dtz, no save);
);
// add rank column for x value
dty << New Column( "x",
Numeric,
"Continuous",
Formula( Col Rank( :Activity ID, :type, :Activity ID ) )
);
// split up latitude and longitude pairs
myCount = 0;
for( k = 1, k <= n rows(dty), k++,
if( dty:type[k] != "latlng",
dty:type[k] = dty:type[k]
,
myCount ++;
if( modulo(myCount,2) == 0,
dty:type[k] = "lat",
dty:type[k] = "lng"
)
);
);
// split tall table to wider table
dt = dty << Split(
Split By( :type ),
Split( :data ),
Group( :x, :Activity ID ),
Remaining Columns( Drop All ),
Sort by Column Property
);
// done with this data table
close(dty, no save);
// update start time from activities table
dt << Update(
With( dtx ),
Match Columns( :Activity ID = :id ),
Add Columns from Update Table( :start_date, :name ),
Replace Columns in Main Table( :start_date, :name )
);
// close activity list
close(dtx, no save);
// clean up time stamp
dt << Begin Data Update;
dt << Recode Column(
dt:start_date,
{Regex( _rcNow, ".*^(.*)Z.*", "\1", GLOBALREPLACE )},
Target Column( :start_date )
);
dt << End Data Update;
dt:start_date << data type(numeric) << Format( "yyyy-mm-ddThh:mm:ss", 19, 0 ) << Input Format( "yyyy-mm-ddThh:mm:ss", 0 );
dt << New Column( "timestamp",
Numeric,
"Continuous",
Format( "yyyy-mm-ddThh:mm:ss", 19, 0 ),
Input Format( "yyyy-mm-ddThh:mm:ss", 0 ),
Formula( (:start_date + :x) - 1 )
);
wait(0);
dt:timestamp << delete formula;
dt << delete column("start_date");
// arrange columns
dt << Move Selected Columns( {:timestamp}, To first );
dt << Move Selected Columns( {:Activity ID, :name}, after(:timestamp) );
// set name
dt << set name("Strava Data");
// sort chronologically
dt << Sort( By( :timestamp ), Order( Ascending ), replace table );
dt << sort( by(:Activity ID), order( ascending ), replace table );コード:
- すべての Strava アクティビティを調べて、名前に「sprint」が含まれるもののみを選択します (大文字と小文字は区別されません)。
- 電力(ワット)を含む多くの測定値を取得しようとします
- この課題で提供される元のデータ セットと同様のデータ テーブルに情報を整理します
Re: JMP ワークフロー 課題 1: 連続電力データからのモチーフの抽出と識別Strava API から直接データを取得してみました。 距離、速度、経度、緯度、高度、勾配など、いくつかの測定値を正常に取得しています。 しかし、私は力を得ていません。 これは、私の Strava アカウントおよび/またはパワーを測定するための機器が不足していることが原因である可能性があると思われます。 この解決策は、Strava アカウントに電力情報がある人にとって必要なものである場合とそうでない場合がありますが、それが正しくない場合、私があまりにもかけ離れているとは想像できません。
Strava API の使用を開始するには、このユーチューブビデオは非常に役に立ち、このコードが Strava からデータを取得する前に必要なアクセス トークンを取得する手順を明確に示しています。 以下のコードで、your_access_token_here を実際のアクセス トークンに置き換えます。
names default to here(1);
accessToken = "your_access_token_here"; // access token from the Strava API
// helpful link to get started with the API: https://www.youtube.com/watch?v=sgscChKfGyg
//////////////////////////////////////////
//////////////////////////////////////////
// get list of activities from Strava
s = New HTTP Request(
URL( "https://www.strava.com/api/v3/athlete/activities?access_token=" || accessToken ),
Method( "GET" ),
Headers( {"Accept: application/json"} )
) << Send;
dtx = json to data table(s);
dtx << set name("Activities List");
// keep only activities marked as sprint
r = dtx << get rows where(
!contains(lowercase(:name), "sprint")
);
dtx << delete rows(r);
if( n rows(dtx) == 0,
close(dtx, no save);
throw();
);
// get list of unique activity ids
summarize( activityList = by(dtx:id));
// get and combine the data streams from each activity
dty = new table();
dty << set name("Strava Data_temp");
for( k = 1, k <= n items(activityList), k++,
actStream = New HTTP Request(
URL( "https://www.strava.com/api/v3/activities/" || activityList[k] || "/streams?keys=latlng,watts,distance,velocity,altitude,velocity_smooth,cadence,temp,moving,grade_smooth&access_token=" || accessToken ),
Method( "GET" ),
Headers( {"Accept: application/json"} )
) << Send;
dtz = json to data table(actStream);
dtz << new column("Activity ID", numeric, nominal, set each value(num(activityList[k])));
dty << concatenate(dtz, append to first table);
close(dtz, no save);
);
// add rank column for x value
dty << New Column( "x",
Numeric,
"Continuous",
Formula( Col Rank( :Activity ID, :type, :Activity ID ) )
);
// split up latitude and longitude pairs
myCount = 0;
for( k = 1, k <= n rows(dty), k++,
if( dty:type[k] != "latlng",
dty:type[k] = dty:type[k]
,
myCount ++;
if( modulo(myCount,2) == 0,
dty:type[k] = "lat",
dty:type[k] = "lng"
)
);
);
// split tall table to wider table
dt = dty << Split(
Split By( :type ),
Split( :data ),
Group( :x, :Activity ID ),
Remaining Columns( Drop All ),
Sort by Column Property
);
// done with this data table
close(dty, no save);
// update start time from activities table
dt << Update(
With( dtx ),
Match Columns( :Activity ID = :id ),
Add Columns from Update Table( :start_date, :name ),
Replace Columns in Main Table( :start_date, :name )
);
// close activity list
close(dtx, no save);
// clean up time stamp
dt << Begin Data Update;
dt << Recode Column(
dt:start_date,
{Regex( _rcNow, ".*^(.*)Z.*", "\1", GLOBALREPLACE )},
Target Column( :start_date )
);
dt << End Data Update;
dt:start_date << data type(numeric) << Format( "yyyy-mm-ddThh:mm:ss", 19, 0 ) << Input Format( "yyyy-mm-ddThh:mm:ss", 0 );
dt << New Column( "timestamp",
Numeric,
"Continuous",
Format( "yyyy-mm-ddThh:mm:ss", 19, 0 ),
Input Format( "yyyy-mm-ddThh:mm:ss", 0 ),
Formula( (:start_date + :x) - 1 )
);
wait(0);
dt:timestamp << delete formula;
dt << delete column("start_date");
// arrange columns
dt << Move Selected Columns( {:timestamp}, To first );
dt << Move Selected Columns( {:Activity ID, :name}, after(:timestamp) );
// set name
dt << set name("Strava Data");
// sort chronologically
dt << Sort( By( :timestamp ), Order( Ascending ), replace table );
dt << sort( by(:Activity ID), order( ascending ), replace table );
コード:
- 名前に「sprint」が含まれ、大文字と小文字が区別されないアクティビティのデータのみが含まれます
- 距離、速度、位置、高度などを含む他の多くの測定値の中で、電力 (ワット) を取得しようとします...
- この課題で提示された元のデータ テーブルと同様の形式でデータを整理します
このコードが、Strava アカウントに電力情報を持っているユーザーにとって機能することを願っています。
Re: JMP ワークフロー 課題 1: 連続電力データからのモチーフの抽出と識別いいね、ブレイディ。
インターバルの開始からドロップアウトまでのかなり短い時間のいくつかについての良い点. ただし、ドロップアウトは最大 2 ~ 3 秒のようです。 今週収集される新しいデータで、これがどのように保持されるかを確認します。
いくつかの追加のトレーニング データが添付されています。 これは非常に安定した状態での約 1:40 の実行ですが、より速いペース (私のクリティカル パワーのほぼ 100%) です。 役立つかもしれません@バイロン_JMPただし、彼の ML モデルを使用します。 ウォームアップ/ウォームダウンの範囲に近づけるために、このトレーニング データ セットに機能を追加することはできますか?
また、HTTP Request (); を使用して Strava API から直接データを取得することに取り組みたい人もいます。 JMPで? 知っている@dieter_pisot以前にこれを行いました。 API を介してデータ プルを簡単に取得できるように、ワークアウトにスプリントなどのタグを付けることができます。

Re: JMP ワークフロー 課題 1: 連続電力データからのモチーフの抽出と識別これは、いくつかの (比較的単純な) 数式列を使用してドロップアウトを補間する別のアプローチです。その多くは、より複雑な補間数式を使用することで排除できますが、トラブルシューティングと数式の作成が容易になります。 (数式とグラフ スクリプトについては、添付の表を参照してください)
結果は、少なくともサンプル データセットでは、必要なものと一致しているようです: (赤い点は削除されます)


仮定がデータに当てはまるかどうかを判断できるように、次のように機能します。
まず、「ドロップアウト」が原因で発生するゼロの補間。 線形補間が選択されました。スプラインと二次方程式が考慮されましたが、結果は私の目に劣るものでした。
下の行 4 ~ 6 の「Cum Zeros Behind」から「Interpolated Power」列に注意してください。
"Cum Zeros Behind" は、ゼロの包括的なルックバックです。
"Cum Zeros Ahead" は、ゼロの包括的な先読みです。
「ゼロ ブロック」は、ブロック内の連続するゼロの数です。
"Groups for Zeros" は 1 から始まり、新しいゼロのグループが発生するたびに増加します
内挿は、行 1 からではなく、ゼロの短い (7 未満の) ブロックがある場合に発生します。
****注: サンプル データでは、補間の最長のケースは 4 つのゼロにまたがり、間隔間の最短の「真の」ブレークは 7 つのゼロでした。 これはエラーの許容範囲が非常に狭く、このアプローチの重大な欠点となる可能性があります。 最短の「真の」ブレークが最長の「ドロップアウト」よりも長いことが重要です。
前述のように、これらの列のほとんどは必要ありませんが、それらがないと補間の式が非常に醜くなり、トラブルシューティングが (はるかに) 難しくなります。

内挿後、少なくとも 7 つの連続したゼロのブロックのみがデータに残ります (ゼロは最初の数行を除いて発生する可能性があります)。
「間隔」列はデータをグループ化します。 1 から始まり、次の場合に増加します。
- タイムスタンプが 100 を超えて変化する、または
-補間されたパワーは0からゼロ以外の値に移動します
「Post-spike」列は、電源がスパイクから定常 (ish) 状態にランプダウンするときに設定されるフラグです。
ゼロに (再) 設定されます。
- 行 1
- 間隔列が変更されるたびに
- ゼロの新しいブロックが発生するたびに
補間されたパワーが 90 を超える値から 90 以下のパワーに移動するたびに、1 に設定されます。 サンプルのデータに基づくと、これは妥当なように思われました。

これで、無視するデータ (「無視」列の目的) を決定できます。 次のいずれかに該当する場合、データは無視されます。
- 間隔の最大値は < 400 です。 (これが「バックグラウンド ノイズ」です)。 繰り返しますが、提供されたデータに基づいています。
- 補間されたパワーが 100 を超え、スパイク後。 (0 に向かって減少するのではなく、「バックグラウンド ノイズ」に移行したことを示します)
- 補間されたパワーは 0 です
"Final Power" は "Interpolated Power" と同じですが、"Ignore" == 1 の場合は Missing に設定されます。
「間隔時間」は、間隔に関連する時間を計算します (つまり、0 から始まり、そこから増加します)。 「ファイナルパワー」と同様に、「無視」== 1 のときは常に欠落に設定されます。
式の評価を無効にしたり、無視されていないデータをサブセット化したりしませんでしたが、これはおそらく次のステップです。
ダンが次にこれをどのように破るかを見るのは興味深いでしょう;)
乾杯、
ブレイディ
Re: JMP ワークフロー 課題 1: 連続電力データからのモチーフの抽出と識別これまでのすべてのソリューションは次のとおりですJMPパブリック:
Re: JMP ワークフロー 課題 1: 連続電力データからのモチーフの抽出と識別これは本当に素晴らしい解決策です。 出力を投稿しましたJMPパブリックローカル データ フィルターを使用して、新しいデータ セット、3 回のトレーニングの実行、およびバイナリ定常状態の特徴列を確認します。
Re: JMP ワークフロー 課題 1: 連続電力データからのモチーフの抽出と識別@Daniel_Valente 、
これが私の挑戦の試みです。
私のアプローチは、モチーフの最初と最後で特徴を識別し、実行領域全体 (最初と最後の間) を識別してから、それぞれにモチーフ ID を割り当てることです。
使用した列数式のコードは次のとおりです。
names default to here(1);
dt = current data table();
// date from time stamp
dt << New Column( "Date[timestamp]",
Numeric,
Nominal,
Format( "yyyy-mm-dd", 19 ),
Input Format( "yyyy-mm-dd" ),
Formula( :timestamp - Time Of Day( :timestamp ) )
);
// rate of change
dt << New Column( "rate of change",
Numeric,
Continuous,
Formula(
Col Moving Average(
Dif(
Col Moving Average(
:power / Col Maximum( :power, :Name( "Date[timestamp]" ) ),
0.6,
5,
5,
Empty(),
:Name( "Date[timestamp]" )
)
)
,
0.95,
5,
5,
Empty(),
:Name( "Date[timestamp]" )
)
)
);
// Start point
dt << New Column( "Start Point",
Numeric,
Continuous,
Formula(
If( Row() == 1,
0,
checkZero = 1 * (:power[Row()] > 0 & :power[Row() - 1] == 0);
foundPeak = 0;
For( k = Row() + 1, k <= N Rows(), k++,
If( :power[k] > Col Quantile( :power, 0.93, :Name( "Date[timestamp]" ) ),
foundPeak = 1;
Break();
);
If( :power[k] == 0, Break() );
);
If( checkZero == 1 & foundPeak == 1,
1,
0
);
)
)
);
// steady state
dt << New Column( "Steady State",
Numeric,
Continuous,
Formula(
ans = 1;
For( k = 0, k <= 10, k++,
ans = Minimum(
ans,
1 * (:rate of change[Row() - k] < 0.003 &
:rate of change[Row() - k] > -0.003)
)
);
ans;
)
);
// Run Region
dt << New Column( "Run Region",
Numeric,
Continuous,
Format( "Best", 12 ),
Formula(
ans = 0;
For( k = Row(), k >= 1, k--,
If( :Start Point[k] == 1,
ans = 1;
Break();
);
If( :Steady State[k] == 0 & :Steady State[k - 1] == 1,
ans = 0;
Break();
);
);
ans;
)
);
// motif ID
dt << New Column( "Motif ID",
Numeric,
Nominal,
Formula(
If(
:Run Region == 1,
Col Cumulative Sum( :Start Point )
)
)
);
// rank timestamp, Motif ID - helps plot each motif
dt << New Column( "Rank[timestamp]",
Numeric,
Continuous,
Formula( Col Rank( :timestamp, :Motif ID ) )
);Re: JMP ワークフロー 課題 1: 連続電力データからのモチーフの抽出と識別いいですね、バイロン -- これはかなり良さそうです。 これらの 2 つのノイズ カーブを選択しています。 週末にかけて追加のデータを収集し、それがどのように公平になるかを確認します.

Re: JMP ワークフロー 課題 1: 連続電力データからのモチーフの抽出と識別良い! それも良い解決策のように聞こえます。 あなたがそれを掘り起こすことができればJSLを投稿してください。私たちはそれを試します.
Re: JMP ワークフロー 課題 1: 連続電力データからのモチーフの抽出と識別ふーむ。 良い質問です。 データの問題について考えるときは、常に仮定を検討することをお勧めします。 また、データを生成しているシステムの所有者にも尋ねてください。
1) セッションごとに 1 つのクールダウン ウォームアップのみ。 ウォームアップはそれほど変わりません。 コーヒーを数杯飲まない限り、平均は約 250 ± 30 ワットです...

2) ご覧のとおり、最高速度を 6 ~ 10 秒間しか維持できず、その後パワーが低下し始めます。 将来的には、より長く/より広くなるでしょう。 それを探る必要があります。
3) 歩行期間後のゼロは含めないでください。 それらはリセット期間です。 最初からほとんど 0 ワットの準備です。 一定時間スプリントする。 減速して停止します。 戻ってください。 次の間隔のセットのためにゼロに戻ります。
4) モチーフ中のゼロは不良データであり、補間する必要があります。 私は止めていません。 センサーが脱落しただけです。 それらを修正するか、欠落としてマークするか、補間する必要があります。そうしないと、その後の分析で問題が発生することがわかりました。
Re: JMP ワークフロー 課題 1: 連続電力データからのモチーフの抽出と識別単純な列式のアプローチを AI モデルを含めるように変更したため、テーブルには非常に複雑な式が 1 つと単純な式がいくつか含まれるようになりました。
添付のスクリプトは、列とグラフを追加します。 モデルは、曲線セグメントを信号またはノイズとしてスコア付けします。
ユニークなデータセットがどのようにスコアリングされるのか興味があります。

Re: JMP ワークフロー 課題 1: 連続電力データからのモチーフの抽出と識別私は、熱衝撃オーブンの温度サイクルを数えていた仕事で、少し似たようなことをしました。 その場合、いくつかの異なる数式列でそれを行ったと思います。作業中のコードを見て、この場合に変更できるかどうかを確認する必要があります。
私の考えが正しく覚えていれば、サイクルの「開始点」を検出し (この場合は 0 電力で、それらの最後の値を取得します)、あるしきい値を超える十分な値があることを確認します (ここでは、5 つの値を超える可能性があります)。 350) その後、サイクルが終了する場所を確認します (80 ~ 100 の間の 20 個の値を持つ平坦な部分)。 そして、実行中の変数 (motid_id、cycle_ongoing など) をいくつか用意し、必要に応じて増やしたりリセットしたりします。
Re: JMP ワークフロー 課題 1: 連続電力データからのモチーフの抽出と識別仮定は重要です。 データについて何を仮定しますか? あなたの問題がどこにあるのかがあります。
1) クールダウン/ウォームアップは何回まで可能ですか? これらの平均パワーを知っていますか? それは異なりますか?
2) モチーフは長さを変えることができます...しかし、それらは常に同じような長さになりますか? これまでのサンプルデータではそうでしたが、今後もそうなるのでしょうか?
3) ゼロはどうですか? モチーフの主役に入れるか入れないか。 それらのすべて、特定の数、それらのどれもありませんか?
4) モチーフの途中にあるゼロについてはどうですか? これらは合法ですか? モチーフの途中にゼロ以外のポイントがある場合はどうなりますか? これらが合法的であるかどうかをどのように判断できますか? 試してみるべきですか?
などなど
乾杯、
ブレイディ
Re: JMP ワークフロー 課題 1: 連続電力データからのモチーフの抽出と識別これは、3番目のファイルが連結されたものです@バイロン_JMPのスクリプト。 同じ現象が起きているようです。 除外するエリアの一部をモチーフにタグ付けしています。 良いことは、同じ場所で失敗したり、同じ失敗モードを使用しているように見えることです。 これは、サイクル グループ 9 と 18、およびサンプル/検出力プロットでも確認できます。


Re: JMP ワークフロー 課題 1: 連続電力データからのモチーフの抽出と識別うおー! 良い@バイロン_JMP . 数式列ソリューションが気に入っています。 スケールしてみました。 それは別の方法で失敗します@ブレイディ_ブレイディのソリューションです。
Motif 9 は少し怪しいように見えます...

Re: JMP ワークフロー 課題 1: 連続電力データからのモチーフの抽出と識別ええとああ! これをスケーリングしようとすると問題が発生します。 現在、2 日間のデータがあります。 Brady のスクリプトを実行して、できれば 16 個のモチーフを特定しました。
新しいデータが入ってきて、スクリプトを実行しました@ブレイディ_ブレイディ書きました。 16 のモチーフを正しく識別しました。 しかし、現在、8番目のものにはいくつかの奇妙な点があります。 アルゴリズムが新しいデータによって混乱しているようです。 タイムスタンプはありますが、このスケールを作成するのに役立つでしょうか?


試しにオフ@バイロン_JMPのソリューションです。
Re: JMP ワークフロー 課題 1: 連続電力データからのモチーフの抽出と識別@ブレイディ_ブレイディこれは、問題を解決するための非常に巧妙な方法です。
3 つの単純な列式でそれを行いました。

new column("time point", formula(If( Row() == 1,
1,
If( :power == 0 & Lag( :time point, 1 ) <= 50,
Lag( :time point, 1 ) + 1,
If( :power == 0,
1,
Lag( :time point, 1 ) + 1
)
)
)));
new column("segments", formula(
If(
Row() == 1, 0,
:time point == 1, Lag( :segments, 1 ) + 1,
Lag( :segments, 1 )
)));
new column("cycle", formula(
If( :time point > 70 | :segments == 0,
.,
:segments
)));Re: JMP ワークフロー 課題 1: 連続電力データからのモチーフの抽出と識別いいね、ブレイディ! すごい仕事。 興味深いのは、データ品質の問題もあるということです。それを滑らかにするために、ピーク モデルを適合させることができます。 たぶん、それらは実際には 0 ではないか、補間されているため、欠落しているとマークされる可能性がありますか?Re: JMP ワークフロー 課題 1: 連続電力データからのモチーフの抽出と識別単一の数式列でどこまで到達できるかは驚くべきことです。
dt << new column("Motif ID", nominal, formula(Col Cumulative Sum(
Row() > 6 & Summation( i = 1, 6, Abs( Lag( :power, i ) ) ) == 0 & :power != 0
)));
ただし、まだクリーンアップが必要です... この方法では、先頭のデータである "0" グループ ID が作成され、必要な最後のモチーフが切り捨てられません。 合計の「6」は、電力変更の前に少なくとも 6 つの連続するゼロに遭遇した場合に新しいグループを指定するためです。
もちろん、バックグラウンド フロア、アタック/ディケイなどとの比較を含む、より洗練された他の多くの方法があります...しかし、このような単純なアプローチが機能する場合は、素晴らしいことです。
以下のスクリプトはこの考え方を取り入れていますが、データセットもクリーンアップしています。 理想的には、最後の数行の値がパラメーター化されます。
names default to here(1);
dt = current data table();
dtSub = dt << subset(selected rows(0), selected columns(0));
//create ID column
dtSub << new column("Motif ID", nominal, formula(Col Cumulative Sum(
Row() > 6 & Summation( i = 1, 6, Abs( Lag( :power, i ) ) ) == 0 & :power != 0
)));
dtSub:MotifID << delete formula;
//the next 2 loops truncate the dataset, assuming it ends like the sample dataset ended.
for(i = nrow(dtSub), abs(dtSub:power[i] - dtSub:power[i-1]) < 20, i--,
dtSub << Delete Rows(i)
);
for(i = nrow(dtSub), abs(dtSub:power[i] - dtSub:power[i-1]) > 5, i--,
dtSub << Delete Rows(i)
);
//delete ID 0 group
dtSub << select where(:Motif ID == 0) << delete rows;
//delete 0 power rows, but not when they lie within a group
i = nrow(dtSub)-1;
while (i > 1,
if(dtSub:Motif ID[i] < dtSub:MotifID[i+1] & dtSub:power[i] == 0,
while( dtSub:power[i] == 0,
dtSub << delete rows(i--);
)
);
i--
);
