在不迭代的情况下在 Pandas 系列中查找连续的、非唯一的切片

Finding contiguous, non-unique slices in Pandas series without iterating

我正在尝试解析我们制造过程的日志文件。大多数时候,该过程是 运行 自动进行的,但偶尔,工程师需要切换到手动模式进行一些更改,然后再切换回反应器软件的自动控制。当设置为手动模式时,日志文件将步骤记录为 "MAN.OP." 而不是数字。下面是一个有代表性的例子。

steps = [1,2,2,'MAN.OP.','MAN.OP.',2,2,3,3,'MAN.OP.','MAN.OP.',4,4]
ser_orig = pd.Series(steps)

这导致

0           1
1           2
2           2
3     MAN.OP.
4     MAN.OP.
5           2
6           2
7           3
8           3
9     MAN.OP.
10    MAN.OP.
11          4
12          4
dtype: object

我需要检测 'MAN.OP.' 并使它们彼此不同。在这个例子中,两个值 == 2 的区域在检测到手动模式部分后应该是一个区域,如下所示:

0                 1
1                 2
2                 2
3     Manual_Mode_0
4     Manual_Mode_0
5                 2
6                 2
7                 3
8                 3
9     Manual_Mode_1
10    Manual_Mode_1
11                4
12                4
dtype: object

我有代码迭代这个系列,并在系列传递给我的对象时产生正确的结果。 setter 是:

@step_series.setter
def step_series(self, ss):
    """
    On assignment, give the manual mode steps a unique name. Leave 
    the steps done on recipe the same.
    """
    manual_mode = "MAN.OP."
    new_manual_mode_text = "Manual_Mode_{}"
    counter = 0
    continuous = False
    for i in ss.index:
        if continuous and ss.at[i] != manual_mode:
            continuous = False
            counter += 1

        elif not continuous and ss.at[i] == manual_mode:
            continuous = True
            ss.at[i] = new_manual_mode_text.format(str(counter))

        elif continuous and ss.at[i] == manual_mode:
            ss.at[i] = new_manual_mode_text.format(str(counter))

    self._step_series = ss

但这会遍历整个数据帧,并且是我的代码中除了通过网络读取日志文件之外最慢的部分。

如何在不遍历整个系列的情况下检测这些非唯一部分并唯一地重命名它们?该系列是从较大的数据框中选择的列,因此如果需要,添加额外的列就可以了。

对于完整的答案,我得到了:

@step_series.setter
def step_series(self, ss):
    pd.options.mode.chained_assignment = None
    manual_mode = "MAN.OP."
    new_manual_mode_text = "Manual_Mode_{}"

    newManOp = (ss=='MAN.OP.') & (ss != ss.shift())
    ss[ss == 'MAN.OP.'] = 'Manual_Mode_' + (newManOp.cumsum()-1).astype(str)

    self._step_series = ss

matplotlib 中有一个函数,它接受一个布尔数组和 returns 一个(开始,结束)对列表。每对代表输入为 True.

的连续区域
import matplotlib.mlab as mlab
regions = mlab.contiguous_regions(ser_orig == manual_mode)
for i, (start, end) in enumerate(regions):
    ser_orig[start:end] = new_manual_mode_text.format(i)
ser_orig

0                 1
1                 2
2                 2
3     Manual_Mode_0
4     Manual_Mode_0
5                 2
6                 2
7                 3
8                 3
9     Manual_Mode_1
10    Manual_Mode_1
11                4
12                4
dtype: object

这是一种方法:

steps = [1,2,2,'MAN.OP.','MAN.OP.',2,2,3,3,'MAN.OP.','MAN.OP.',4,4]
steps = pd.Series(steps)

newManOp = (steps=='MAN.OP.') & (steps != steps.shift())
steps[steps=='MAN.OP.'] += seq.cumsum().astype(str)

>>> steps
0            1
1            2
2            2
3     MAN.OP.1
4     MAN.OP.1
5            2
6            2
7            3
8            3
9     MAN.OP.2
10    MAN.OP.2
11           4
12           4
dtype: object

要获得您列出的确切格式(从零开始,而不是从一个开始,然后从 "MAN.OP." 更改为 "Manual_mode_"),只需调整最后一行:

steps[steps=='MAN.OP.'] = 'Manual_Mode_' + (seq.cumsum()-1).astype(str)

>>> steps
0                 1
1                 2
2                 2
3     Manual_Mode_0
4     Manual_Mode_0
5                 2
6                 2
7                 3
8                 3
9     Manual_Mode_1
10    Manual_Mode_1
11                4
12                4
dtype: object

a pandas enhancement request 用于连续 groupby,这将使此类任务更简单。