Как красиво напечатать quadtree в python?

У меня есть код, который может сделать дерево квадрантов из точек данных. Я знаю, как распечатать двоичное дерево с косой чертой, но я даже не знаю, с чего начать печатать/выводить дерево с 4 дочерними элементами вместо 2, чтобы иметь возможность визуализировать свое дерево.

Я тестировал это, используя мою функцию search_pqtreee. Например, чтобы перечислить все точки в северо-восточном квадранте, я могу проверить это, составив список вроде: [search_pqtree(q.ne,p) для p в точках]

#The point import is a class for points in Cartesian coordinate systems
from point import *

class PQuadTreeNode():
    def __init__(self,point,nw=None,ne=None,se=None,sw=None):
        self.point = point
        self.nw = nw
        self.ne = ne
        self.se = se
        self.sw = sw
    def __repr__(self):
        return str(self.point)
    def is_leaf(self):
        return self.nw==None and self.ne==None and \
            self.se==None and self.sw==None

def search_pqtree(q, p, is_find_only=True):
    if q is None:
        return
    if q.point == p:
        if is_find_only:
            return q
        else:
            return
    dx,dy = 0,0
    if p.x >= q.point.x:
        dx = 1
    if p.y >= q.point.y:
        dy = 1
    qnum = dx+dy*2
    child = [q.sw, q.se, q.nw, q.ne][qnum]
    if child is None and not is_find_only:
        return q
    return search_pqtree(child, p, is_find_only)

def insert_pqtree(q, p):
    n = search_pqtree(q, p, False)
    node = PQuadTreeNode(point=p)
    if p.x < n.point.x and p.y < n.point.y:
        n.sw = node
    elif p.x < n.point.x and p.y >= n.point.y:
        n.nw = node
    elif p.x >= n.point.x and p.y < n.point.y:
        n.se = node
    else:
        n.ne = node

def pointquadtree(data):
    root = PQuadTreeNode(point = data[0])
    for p in data[1:]:
        insert_pqtree(root, p)
    return root

#Test
data1 = [ (2,2), (0,5), (8,0), (9,8), (7,14), (13,12), (14,13) ]

points = [Point(d[0], d[1]) for d in data1]
q = pointquadtree(points)
print([search_pqtree(q.ne, p) for p in points])

Я пытаюсь сказать, что если бы я печатал двоичное дерево, оно могло бы выглядеть так:

       (2, 2)                          
      /      \                         
   (0, 5)  (8, 0)                      
   /    \ /      \     

Есть ли способ написать функцию, которая выводит по 4 строки? Или, может быть, распечатать его боком?

Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
1
0
1 184
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Поскольку вы классифицировали свой вопрос с помощью ГИС и пространственных данных, эта проблема заставила меня подумать о карте с северо-востоком, северо-западом, юго-востоком и юго-западом в каждом углу.

Дерево квадрантов с одним узлом будет просто:

(0,0)

Дерево квадрантов с двумя узлами будет:

      .|( 1, 1)
----( 0, 0)----
      .|.

С 3 узлами в глубину, которые будут идти к:

               |      .|( 2, 2)
               |----( 1, 1)----
              .|      .|.
------------( 0, 0)------------
              .|.
               |
               |

Я реализовал эту идею с некоторыми изменениями в вашем коде, чтобы упростить ее:

  • Я добавил тривиальный класс точек с методом __repr__, который мне нужен для форматирования чисел.
  • Я превратил квадранты в словарь, чтобы иметь возможность зацикливаться на них.
  • Я думал, что мне понадобится метод get_depth, но он не используется...

Я также думаю, что функции search и insert должны быть методами класса PQuadTreeNode, но я оставляю это вам в качестве упражнения :)

Реализация работает со следующими шагами:

  • Если дерево квадрантов является листом, его карта является центральной точкой.
  • Получить карты 4 квадрантов (если пусто, это точка)
  • Нормализуйте их, используя размер наибольшего, и поместите их ближе к центру родителя.
  • Объедините 4 квадранта с точкой дерева квадрантов в центре.

Это, конечно, очень рекурсивно, и я не пытался оптимизировать. Если числа имеют длину больше 2 (например, 100 или -10), вы можете настроить переменную num_length.

num_length = 2
num_fmt = '%' + str(num_length) + 'd'

class Point():
    def __init__(self,x=None,y=None):
        self.x = x
        self.y = y
    def __repr__(self):
        return '(' + (num_fmt % self.x) + ',' + (num_fmt % self.y) + ')'

def normalize(corner, quadmap, width, height):
    old_height = len(quadmap)
    old_width = len(quadmap[0])
    if old_height == height and old_width == width:
        return quadmap
    else:
        blank_width = width - old_width
        if corner == 'nw':
            new = [' '*width for i in range(height - old_height)]
            for line in quadmap:
                new.append(' '*blank_width + line)
        elif corner == 'ne':
            new = [' '*width for i in range(height - old_height)]
            for line in quadmap:
                new.append(line + ' '*blank_width)
        elif corner == 'sw':
            new = []
            for line in quadmap:
                new.append(' '*blank_width + line)
            for i in range(height - old_height):
                new.append(' '*width)
        elif corner == 'se':
            new = []
            for line in quadmap:
                new.append(line + ' '*blank_width)
            for i in range(height - old_height):
                new.append(' '*width)
        return new

class PQuadTreeNode():
    def __init__(self,point,nw=None,ne=None,se=None,sw=None):
        self.point = point
        self.quadrants = {'nw':nw, 'ne':ne, 'se':se, 'sw':sw}

    def __repr__(self):
        return '\n'.join(self.get_map())

    def is_leaf(self):
        return all(q == None for q in self.quadrants.values())

    def get_depth(self):
        if self.is_leaf():
            return 1
        else:
            return 1 + max(q.get_depth() if q else 0 for q in self.quadrants.values())

    def get_map(self):
        if self.is_leaf():
            return [str(self.point)]
        else:
            subquadmaps = {
                sqn:sq.get_map() if sq else ['.']
                for sqn, sq
                in self.quadrants.items()
            }
            subheight = max(len(map) for map in subquadmaps.values())
            subwidth = max(len(mapline) for map in subquadmaps.values() for mapline in map)
            subquadmapsnorm = {
                sqn:normalize(sqn, sq, subwidth, subheight)
                for sqn, sq
                in subquadmaps.items()
            }
            map = []
            for n in range(subheight):
                map.append(subquadmapsnorm['nw'][n] + '|' + subquadmapsnorm['ne'][n])
            map.append('-' * (subwidth-num_length-1) + str(self.point) + '-' * (subwidth-num_length-1))
            for n in range(subheight):
                map.append(subquadmapsnorm['sw'][n] + '|' + subquadmapsnorm['se'][n])
            return map

def search_pqtree(q, p, is_find_only=True):
    if q is None:
        return
    if q.point == p:
        if is_find_only:
            return q
        else:
            return
    dx,dy = 0,0
    if p.x >= q.point.x:
        dx = 1
    if p.y >= q.point.y:
        dy = 1
    qnum = dx+dy*2
    child = [q.quadrants['sw'], q.quadrants['se'], q.quadrants['nw'], q.quadrants['ne']][qnum]
    if child is None and not is_find_only:
        return q
    return search_pqtree(child, p, is_find_only)

def insert_pqtree(q, p):
    n = search_pqtree(q, p, False)
    node = PQuadTreeNode(point=p)
    if p.x < n.point.x and p.y < n.point.y:
        n.quadrants['sw'] = node
    elif p.x < n.point.x and p.y >= n.point.y:
        n.quadrants['nw'] = node
    elif p.x >= n.point.x and p.y < n.point.y:
        n.quadrants['se'] = node
    else:
        n.quadrants['ne'] = node

def pointquadtree(data):
    root = PQuadTreeNode(point = data[0])
    for p in data[1:]:
        insert_pqtree(root, p)
    return root

#Test
data1 = [ (2,2), (0,5), (8,0), (9,8), (7,14), (13,12), (14,13) ]

points = [Point(d[0], d[1]) for d in data1]
q = pointquadtree(points)
print(q)

С вашими примерными данными:

                               |               |      .|(14,13)
                               |               |----(13,12)----
                               |        ( 7,14)|      .|.
                               |------------( 9, 8)------------
                               |              .|.
                               |               |
                        ( 0, 5)|               |
----------------------------( 2, 2)----------------------------
                              .|( 8, 0)
                               |
                               |
                               |
                               |
                               |
                               |

Скажите мне, если вы найдете это полезным!

Это было очень полезно, спасибо за четкое объяснение!

JohnMurphy27 27.04.2019 21:59

Другие вопросы по теме