稳健地分割包含噪声数据的 csv 文件

稳健地分割包含噪声数据的 csv 文件

假设我有一个包含以下列的 csv 文件:

timestamp,cpu,memory

实际数据形状的示例如下图:

cpu/mem csv 数据图表

通过这种方式将数据可视化,人眼就可以轻松看出 4 个 cpu 密集型活动的开始和结束位置。

什么是一个简单的方法,使用标准的unix命令行工具,理想情况下不诉诸Roctave等等,“grep”仅显示文件中表示这 4 个活动的开始和结束的 8 行,基于“cpu”列?

显然,这有点模糊,并且数据中存在噪声,但任何为我提供接近开始/结束的 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

理想情况下,这将导致以下两行。

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

答案1

一种方法是使用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

答案2

通过扩展 jordanm 的方法,我能够构建出令人惊讶的强大功能,而无需借助统计数据。遗憾的是,脚本变得有点长,但这个已经完成了,现在我可以根据需要随时使用它,只需找出正确的参数即可。

我在十几个实际数据文件上进行了测试,其中一些像这个一样混乱:

凌乱的 cpu 和内存 csv 图表

这里的技巧是使用MIN_DURATION变量,这有助于忽略最多指定行数的临时峰值和暴跌。

用法:

grep-begin-end FIELD_SEPARATOR FIELD_INDEX THRESHOLD_UP THRESHOLD_DOWN MIN_DURATION ...

例子:

grep-begin-end , 2 30 4 5 file.csv

grep 开始结束

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;
        }
    }
}' "$@"

答案3

我想你可以写一个小脚本,如果当前值与上一个值相差超过 N,则打印出来。由于你的数据似乎非常离散,并且变化发生在一个测量到下一个测量之间,所以应该可以工作。

就像是

awk -F, -v thr=100 '{if ($2-last>thr || last-$2>thr){print;} last=$2;}' foo.csv

相关内容