首页 > 解决方案 > 在网格中标记相邻单元面

问题描述

我有一个熊猫数据框中的节点列表,如下所示:

    row  col
    ... 
36  182  240          
35  182  241          
34  182  242          
33  182  243         
58  183  220          
32  183  244          
31  183  245          
30  183  246          
29  183  247          
    ...

该节点的网格如下所示:

在此处输入图像描述

我的代码标记了 X-ed 单元格的每个面,因此如果它连接到调整 X-ed 单元格,则标记为 0,如果未连接(打开),则标记为 1。代码无法正常工作沿着一些边缘:

df["Front Face"] = 1
df["Back Face"]  = 1
df["Right Face"] = 1
df["Left Face"]  = 1

df = df.sort_values(by=['row','col'], ascending=True)
df = df.reset_index(drop=True)
for ix1 in df.index:
    try:
        if df["col"][ix1] == df["col"][ix1 + 1] - 1:
            df["Right Face"][ix1] = 0
            df["Left Face"][ix1 + 1]  = 0
        if df["col"][ix1] == df["col"][ix1 - 1] + 1:
            df["Left Face"][ix1] = 0
            df["Right Face"][ix1 - 1]  = 0
    except:
        pass


df= df.sort_values(by=['col','row'], ascending=True)
df= df.reset_index(drop=True)
for ix2 in df.index:
    try:
        if df["row"][ix2] == df["row"][ix2 + 1] - 1:
            df["Back Face"][ix2] = 0
            df["Front Face"][ix2 + 1]  = 0
        if df["row"][ix2] == df["row"][ix2 - 1] + 1:
            df["Front Face"][ix2] = 0
            df["Back Face"][ix2 - 1]  = 0
    except:
        pass

这是单元格 182,243 和 183,244 缺少一个标签的输出的一部分:

    row  col  Front Face  Back Face  Right Face  Left Face
36  182  240          1           1          0           0
35  182  241          1           1          0           0
34  182  242          1           1          0           0
33  182  243          1           0          1           0
58  183  220          1           0          1           1
32  183  244          0           1          0           1
31  183  245          1           1          0           0
30  183  246          1           1          0           0
29  183  247          1           1          0           0

我在这里圈出了图中有问题的单元格:

在此处输入图像描述

标签: pythonpandasgrid

解决方案


我假设您标记中的每一行都df占据了一个位置,并且您希望将相邻的单元格标记为前、后、左或右。

如果是这样,您可以以矢量化的方式执行此操作,但我必须承认:我在让索引和 numpy 广播正常工作方面付出了很多努力。

# A random 5 * 10 matrix with 10% of rows marked as "occupied"
n, m = 5, 10
count = int(n * m * 0.1)

df = pd.DataFrame({
    'row': np.random.randint(n, size=count),
    'col': np.random.randint(m, size=count)
}).drop_duplicates()

让我们构建result数据框:

from itertools import product

# Every row in `df` marks an occupied position
result = df.set_index(['row', 'col']).assign(Occupied = True)

# Now expand `result` into the full matrix
idx = product(range(n), range(m))
result = result.reindex(idx, fill_value=False)

# Every cell is Open initially
for col in ['Front Face', 'Back Face', 'Right Face', 'Left Face']:
    result[col] = 1

# Now start to build out a list of blocked cells
occupied = result.query('Occupied').index.to_frame().to_numpy()
valid_index = result.index

faces = {
    'Front Face': [-1, 0],
    'Back Face': [1, 0],
    'Left Face': [0, -1],
    'Right Face': [0, 1]
}

for face, offset in faces.items():
    blocked = valid_index.intersection([tuple(i) for i in occupied + offset])
    result.loc[blocked, face] = 0

为了说明结果,让我们构建一个辅助函数:

from IPython.display import display

def illustrate(result):   
    display_df = result['Occupied'].map({True: 'x', False: ''}).reset_index()
    display_df = display_df.pivot(index='row', columns='col', values='Occupied')
    
    is_open = result[['Front Face', 'Back Face', 'Right Face', 'Left Face']].all(axis=1)
    style_df = (
        is_open.map({
            True: 'background-color: white',
            False: 'background-color: rgba(255,0,0,0.2)'
        })
        .unstack()
    )
    
    display(display_df.style.apply(lambda _: style_df, axis=None))

illustrate(result)

结果(红色的单元格在任何面上都有一个 0):

标记网格


推荐阅读