英文:
3D connected components with periodic boundary conditions
问题
我试图在地球上识别连接的特征(即,空间-时间上的球面上的特征)。cc3d 包已经帮助我解决了90%的问题,但我在处理日期边界时遇到了困难(即,在三个维度中的一个维度上的周期性边界条件)。
在二维地图上,我的方法的问题变得明显(请注意在南极附近0经度附近连接但标记不同的区域):
这是因为数据在经度从0到360之间定义(虽然我在这里显示它们从-180到180,以更明显地显示问题)。
对于这些问题,我通常的两种解决方案不起作用:
- 反经线移至太平洋只是转移了问题,因此没有帮助。
- 在右边界拼接数据的副本也没有帮助,因为它会导致原始左侧和右侧粘贴数据之间的标签模糊。
MWE
在二维中,问题的分解应该如下:
import cc3d
import numpy as np
import matplotlib.pyplot as plt
arr = np.sin(np.random.RandomState(0).randn(100).reshape(10, 10)) > .3
labels = cc3d.connected_components(arr)
map = plt.pcolormesh(labels, vmin=.5)
map.cmap.set_under('none')
在这里,右上角的黄色结构应该与顶部的其余结构连接在一起,底部的两个结构也是如此。请记住,任何有用的解决方案也应适用于三维中的连接特征。
对于如何解决这个问题,欢迎任何帮助。
英文:
I am trying to identify connected features on the globe (i.e., in space-time on a sphere). The cc3d package has gotten me 90% of the way but I am struggling to deal with the date border (i.e., the periodic boundary conditions in one of the three dimensions).
On a 2D map, the problem of my approach becomes apparent (note the connected buy differently labelled region around 0 longitude near the South Pole):
This appears because the data are defined on longitudes from 0 to 360 (while I show shown them from -180 to 180 here to make the problem more apparent).
My two typical solutions for these kinds of problems do not work:
- Flipping the anti-meridian to the pacific just shifts the problem and therefore does not help.
- Concatenating a copy of the data at right hand boundary does also not help because it leads to ambiguity in the labels between the original left side and the pasted data on the right
MWE
A break-down of the problem in 2 dimensions should be the following:
import cc3d
import numpy as np
import matplotlib.pyplot as plt
arr = np.sin(np.random.RandomState(0).randn(100).reshape(10, 10)) > .3
labels = cc3d.connected_components(arr)
map = plt.pcolormesh(labels, vmin=.5)
map.cmap.set_under('none')
Here the yellow structure in the right top should be connected to the rest of the top structure and same for the two structures at the bottom. Please keep in mind that any helpful solution should also work for connected features in 3 dimensions.
Any help on how to approach this is appreciated.
答案1
得分: 0
好的,以下是翻译的代码部分:
好的,我有一个修复输出的方法。它相当慢,需要运行`connected_components`两次,但对我来说有效。我把它放在这里,以防对其他人有帮助。
我将在此处放置MWE的修复版本,但对于完整的3D时空数据(独立处理每一天),它的效果是等效的。我正在处理的数据约定是,经度是最后一个维度,所以(时间,纬度,经度),但它在x轴上绘制,因此我在MWE示例中的所有函数调用中执行了`swapaxes`。
函数
```python
def plot(arr):
map = plt.pcolormesh(arr, vmin=.5)
map.cmap.set_under('none')
for xx in range(len(arr)):
for yy in range(len(arr)):
plt.text(yy+.5, xx+.5, arr[xx, yy],
ha="center", va="center", color="k")
def cut_stitch(arr): # 相当于切换反子午线 [0, 360[ -> [-180, 180[
idx = len(arr) // 2
return np.concatenate([arr[idx:], arr[:idx]], axis=0)
def fix_date_border(arr, arr_r):
mask = arr == 0
mapping = {}
for key, value in zip(arr_r[~mask], arr[~mask]):
try:
mapping[key].add(value)
except KeyError:
mapping[key] = {value}
# 如果每个键只有一个唯一值,那么它是一对一映射,没有问题
conflicts = [list(values) for values in mapping.values() if len(values) != 1]
print(mapping) # 调试
for conflict in conflicts:
idx = np.any([arr == cc for cc in conflict[1:]], axis=0)
arr[idx] = conflict[0] # 在原始(即第一个)数组中重新分配
return arr
在重新排列的字段上第二次运行组件标记,然后再次重新排列:
arr2 = cut_stitch(arr.swapaxes(0, 1)).swapaxes(0, 1)
labels2 = cut_stitch(cc3d.connected_components(arr2).swapaxes(0, 1)).swapaxes(0, 1)
plot(labels) # 原始
plot(labels2)
修复标签:
labels_fixed = fix_date_border(labels.swapaxes(0, 1), labels2.swapaxes(0, 1)).swapaxes(0, 1)
# {2: {1, 2}, 7: {8, 7}, 5: {4}, 3: {3}, 1: {1}, 4: {5}, 8: {7}, 6: {6}}
plot(labels_fixed, labels=True)
请注意,在修复后的数组中,标签将不再严格连续!
<details>
<summary>英文:</summary>
Okay I have a fix for the output. Its quite slow and requires to run `connected_components` twice but works for me. I'm putting it here in case its helpful for anyone later.
I'll put the fix for the MWE here but it works equivalently for full 3D space-time data (independently for each day). **The convention for the data I'm working with is that the longitude is the last dimension, so (time, latitude, longitude) but it is plotted on the x-axis, therefore I do a `swapaxes` for all function calls in the MWE example.**
Functions
```python
def plot(arr):
map = plt.pcolormesh(arr, vmin=.5)
map.cmap.set_under('none')
for xx in range(len(arr)):
for yy in range(len(arr)):
plt.text(yy+.5, xx+.5, arr[xx, yy],
ha="center", va="center", color="k")
def cut_stitch(arr): # equivalent to switching the antimeridian [0, 360[ -> [-180, 180[
idx = len(arr) // 2
return np.concatenate([arr[idx:], arr[:idx]], axis=0)
def fix_date_border(arr, arr_r):
mask = arr == 0
mapping = {}
for key, value in zip(arr_r[~mask], arr[~mask]):
try:
mapping[key].add(value)
except KeyError:
mapping[key] = {value}
# if there is only one unique value per key its a one-to-one mapping and no problem
conflicts = [list(values) for values in mapping.values() if len(values) != 1]
print(mapping) # DEBUG
for conflict in conflicts:
idx = np.any([arr == cc for cc in conflict[1:]], axis=0)
arr[idx] = conflict[0] # reassign in original (i.e., first) array
return arr
Run component labelling a second time on the rearranged field, then rearrange again:
arr2 = cut_stitch(arr.swapaxes(0, 1)).swapaxes(0, 1)
labels2 = cut_stitch(cc3d.connected_components(arr2).swapaxes(0, 1)).swapaxes(0, 1)
plot(labels) # original
plot(labels2)
Fix the labels:
labels_fixed = fix_date_border(labels.swapaxes(0, 1), labels2.swapaxes(0, 1)).swapaxes(0, 1)
# {2: {1, 2}, 7: {8, 7}, 5: {4}, 3: {3}, 1: {1}, 4: {5}, 8: {7}, 6: {6}}
plot(labels_fixed, labels=True)
Note that the labels will no longer be strictly consecutive in the fixed array!
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论