Command-Line
穩健地分割包含雜訊數據的 csv 文件
假設我有一個包含這些列的 csv 文件:
timestamp,cpu,memory
實際數據形狀的一個範例如下圖:
以這種方式視覺化數據後,人眼很容易分辨出 4 個 CPU 密集型活動的開始和結束位置。
什麼是一種簡單的方法,使用標準的 unix cmd line 工具,理想情況下不訴諸
R
oroctave
等,根據“cpu”列**“grep”僅表示這 4 個活動的開始和結束的文件的 8 行**?顯然這有點模糊,並且數據中有噪音,但是任何給我 8 行接近開始/結束的解決方案都值得讚賞。
csv 文件的簡短摘錄,僅顯示這 4 次活動中的第一次:
10:44:21.310,0,53567488 10:44:22.310,1.56257,53575680 10:44:23.326,0,53854208 10:44:24.325,34.3761,57405440 10:44:25.325,73.43985,61747200 10:44:26.325,3.1251,69459968 10:44:27.325,0,69459968 10:44:28.325,0,69459968 10:44:29.325,65.6271,74756096 10:44:30.325,53.1267,77783040 ... 10:50:56.450,35.93865,142700544 10:50:57.450,10.93785,142897152 10:50:58.450,1.56255,142897152 10:50:59.450,0,142897152 10:51:00.450,0,142897152 10:51:01.450,0,142897152
理想情況下,這將導致以下 2 行。
10:44:24.325,34.3761,57405440 10:50:58.450,1.56255,142897152
10:44:26.325
但是,由於和之間的幾秒鐘內沒有 cpu 活動10:44:28.325
,我不希望答案那麼聰明並且做類似於“平滑”數據的事情。因此,如果像上面這樣的 csv 片段會產生以下 4 行,那就足夠了。10:44:24.325,34.3761,57405440 10:44:26.325,3.1251,69459968 10:44:29.325,65.6271,74756096 10:50:58.450,1.56255,142897152
一種方法是使用
awk
. 您可以設置門檻值以獲取達到門檻值的第一行和低於門檻值的最後一行。像這樣的東西可能會起作用:awk -F, -vthreshold_up=20 -vthreshold_down=10 'BEGIN { cur = "gt"; } { if (cur == "gt" && $2 > threshold_up) { print; cur = "lt"; } else if (cur = "lt" && $2 < threshold_down) { print; cur = "gt"; } }' file.csv
擴展 jordanm 的方法,我能夠建構出令人驚訝的健壯的東西,而無需求助於統計數據。不幸的是,腳本有點長了,但這已經完成了,現在我可以根據需要經常使用它,只需找出正確的參數。
我在十幾個實際數據文件上對其進行了測試,其中一些和這個一樣混亂:
這裡的技巧是使用
MIN_DURATION
變數,它有助於忽略高達指定行數的臨時峰值和下降。用法:
grep-begin-end FIELD_SEPARATOR FIELD_INDEX THRESHOLD_UP THRESHOLD_DOWN MIN_DURATION ...
例子:
grep-begin-end , 2 30 4 5 file.csv
抓地力開始結束
FIELD_SEPARATOR=$1 FIELD_INDEX=$2 THRESHOLD_UP=$3 THRESHOLD_DOWN=$4 MIN_DURATION=$5 shift 5 awk -F$FIELD_SEPARATOR -vthreshold_up=$THRESHOLD_UP -vthreshold_down=$THRESHOLD_DOWN 'BEGIN { cur = "gt"; } { val = $'$FIELD_INDEX'; # strip of double quotes and convert to number if (substr(val, 1, 1) == "\"") { val = 0 + substr(val, 2, length(val) - 3); } else { val = 0 + val; } buf = ""; if (cur == "gt") { if (val >= threshold_up) { if (buf == "") buf = $0; if (duration >= '$MIN_DURATION') { print buf; cur = "lt"; duration = 0; buf = ""; } else { duration++; } } else { duration = 0; } } else if (cur == "lt") { if (val <= threshold_down) { if (buf == "") buf = $0; if (duration >= '$MIN_DURATION') { print buf; cur = "gt"; duration = 0; buf = ""; } else { duration++; } } else { duration = 0; } } }' "$@"