Хранение информации о точках в 3d пространстве

Я пишу некоторый код (пока просто для удовольствия) на Python, который будет хранить некоторые данные о каждой точке в трехмерном пространстве. В основном мне нужен трехмерный матричный объект, в котором хранятся произвольные объекты, которые позволят мне делать некоторые расширенные выборки, например:

  • Получите точку, где x=1,y=2,z=3.
  • Получение всех точек, где y=2.
  • Получение всех точек в пределах 3 единиц позиции x=1,y=2,z=3.
  • Получение всех точек, где point.getType() == "Foo"

Во всем вышеперечисленном мне нужно было бы получить какой-то вывод, который дал бы мне исходное положение в пространстве и данные, хранящиеся в этой точке.

По-видимому, numpy может делать то, что я хочу, но кажется, что он очень оптимизирован для научных вычислений, и до сих пор ускользал от меня вопрос о том, как получить данные, как я хочу выше.

Есть ли лучшая альтернатива, или я должен вернуться к тому, чтобы биться головой о стену numpy? :)

РЕДАКТИРОВАТЬ: еще немного информации, первые три ответа заставили меня понять, что я должен включить: я не беспокоюсь о производительности, это просто доказательство концепции, где я бы предпочел чистый код хорошей производительности. У меня также будут данные для каждой точки в заданном трехмерном пространстве, поэтому я думаю, что запасная матрица — это плохо?


person Community    schedule 26.05.2009    source источник
comment
Да, просто используйте трехмерный массив, более сложные структуры предназначены только для оптимизации некоторых операций в некоторых конкретных случаях.   -  person Luper Rouch    schedule 26.05.2009
comment
когда вы говорите о точках в пространстве, я предполагаю, что x, y, x являются непрерывными переменными, например. поплавки, но ваш пример подразумевает целые числа. Если целые, разреженная матрица в порядке. В противном случае используйте кортежи или объекты. Лично я бы порекомендовал перелезть через стену numpy. Это зеленое пастбище на другой стороне!   -  person Paul    schedule 27.05.2009


Ответы (8)


Вот еще один распространенный подход

class Point( object ):
    def __init__( self, x, y, z, data ):
        self.x, self.y, self.z = x, y, z
        self.data = data
    def distFrom( self, x, y, z )
        return math.sqrt( (self.x-x)**2 + (self.y-y)**2 + (self.z-z)**2 )

database = [ Point(x,y,z,data), Point(x,y,z,data), ... ]

Давайте посмотрим на ваши варианты использования.

Получите точку, где x=1,y=2,z=3.

[ p for p in database if (p.x, p.y, p.z) == ( 1, 2, 3 ) ]

Получение всех точек, где y=2.

[ p for p in database if p.y == 2 ]

Получение всех точек в пределах 3 единиц позиции x=1,y=2,z=3.

[ p for p in database if p.distFrom( 1, 2, 3 ) <= 3.0 ]

Получение всех точек, где point.getType() == "Foo"

[ p for p in database if type(p.data) == Foo ]
person S.Lott    schedule 26.05.2009
comment
Спасибо! Я запутался в структурах данных, более сложных, чем нужно. Это работает очень хорошо и позволяет мне делать любой выбор данных, как я хочу каждый раз. - person ; 28.05.2009

Что ж... Если вы хотите действительно заполнить это пространство, то вам, вероятно, лучше всего подойдет плотно упакованная матричная структура, в основном воксели.

Если вы не ожидаете заполнить его, поищите что-то более оптимизированное. Я бы начал с рассмотрения octrees, которые часто используются для подобных вещей.

person unwind    schedule 26.05.2009
comment
воксели — это не структура данных, это способ отображения трехмерной сетки. - person Luper Rouch; 26.05.2009

Одним из преимуществ numpy является то, что он невероятно быстр, например. вычисление PageRank матрицы смежности 8000x8000 занимает миллисекунды. Несмотря на то, что numpy.ndarray будет принимать только числа, вы можете хранить сопоставления чисел/идентификаторов объектов во внешней хеш-таблице, то есть в словаре (который, опять же, является высокооптимизированной структурой данных).

Нарезка будет такой же простой, как нарезка списка в python:

>>> from numpy import arange

>>> the_matrix = arange(64).reshape(4, 4, 4)
>>> print the_matrix[0][1][2]
    6
>>> print the_matrix[0][1]
    [4 5 6 7]
>>> print the_matrix[0]
    [[ 0  1  2  3]
    [ 4  5  6  7]
    [ 8  9 10 11]
    [12 13 14 15]]

Если вы обернете некоторые из ваших желаемых функций (расстояний) вокруг некоторой основной матрицы и хеша сопоставления id-object, вы можете запустить свое приложение в течение короткого периода времени.

Удачи!

person miku    schedule 26.05.2009
comment
Я думал, что ndarray тоже может хранить только числа, но он может хранить что угодно! numpy.ndarray([w,h,d],object) Это то, с чем я сейчас борюсь, но это достаточно раздражает, я разместил здесь лучшие решения, которые требуют меньшего знания матричных вещей! - person ; 26.05.2009
comment
хорошо, тогда мое предложение несколько бессмысленно, я его удалю; но почему вы хотите избежать matrix stuff; матрицы, я бы не сказал, забавные, но несколько аккуратные. а для нарезки, доступа ndarray отлично подходит - так почему бы его не использовать? - person miku; 26.05.2009
comment
На самом деле, это далеко не бессмысленно, это метод, который я использую сейчас. Я просто сохраняю свой собственный класс в каждой точке. Единственная причина, по которой мне это не нравится, заключается в том, что многие numpy, кажется, предполагают, что вы знаете матричную математику/терминологию, а поскольку я этого не знаю, это просто что-то еще, на что можно наткнуться. - person ; 26.05.2009

Вот подход, который может сработать.

Каждая точка представляет собой 4-кортеж (x, y, z, данные), и ваша база данных выглядит следующим образом:

database = [ (x,y,z,data), (x,y,z,data), ... ]

Давайте посмотрим на ваши варианты использования.

Получите точку, где x=1,y=2,z=3.

[ (x,y,z,data) for x,y,z,data in database if (x,y,z) == (1,2,3) ]

Получение всех точек, где y=2.

[ (x,y,z,data) for x,y,z,data in database if y == 2 ]

Получение всех точек в пределах 3 единиц позиции x=1,y=2,z=3.

[ (x,y,z,data) for x,y,z,data in database if math.sqrt((x-1)**2+(y-2)**2+(z-3)**2)<=3.0 ]

Получение всех точек, где point.getType() == "Foo"

[ (x,y,z,data) for x,y,z,data in database if type(data) == Foo ]
person S.Lott    schedule 26.05.2009
comment
Это будет безумно медленно для поиска! - person Ed James; 26.05.2009
comment
@Ed Woodcock: Так работает RDBMS, поэтому для этой архитектуры есть прецедент. Кроме того, к этому можно добавить индексирование, если этого требует объем данных. Поскольку мы не знаем объем данных или частоту запросов, мы еще не знаем достаточно, чтобы отвергнуть это. - person S.Lott; 26.05.2009
comment
Требование к произвольному объекту здесь представляет собой небольшую проблему: если он неизменяем, для изменения записи требуется выполнить три шага: 1. удалить старый кортеж 2. создать новый кортеж 3. добавить новый кортеж. Конечно, это не будет проблемой, если вы используете свой собственный класс для объекта данных. - person RoadieRich; 26.05.2009
comment
@RoadieRich: не имеет значения, к какому классу относятся дополнительные данные. Мы перестраиваем группы кортежей. Но эти кортежи содержат одни и те же исходные объекты, неизмененные благодаря повторному включению в многочисленные новые кортежи. Базовые объекты во всех этих кортежах никогда не меняются. - person S.Lott; 26.05.2009

Вы можете выполнить первые 2 запроса с нарезкой в ​​numpy:

a = numpy.zeros((4, 4, 4))
a[1, 2, 3] # The point at x=1,y=2,z=3
a[:, 2, :] # All points where y=2

Для третьего, если вы имеете в виду «получение всех точек в сфере радиусом 3 с центром в x = 1, y = 2, z = 3», вам нужно будет написать пользовательскую функцию для этого; если вам нужен куб, вы можете продолжить нарезку, например:

a[1:3, 1:3, 1:3] # The 2x2x2 array sliced from the center of 'a'

Для четвертого запроса, если единственными данными, хранящимися в вашем массиве, являются типы ячеек, вы можете закодировать их как целые числа:

FOO = 1
BAR = 2
a = numpy.zeros((4, 4, 4), dtype="i")
a[1, 2, 3] = FOO
a[3, 2, 1] = BAR
def filter(a, type_code):
    coords = []
    for z in range(4):
        for y in range(4):
            for x in range(4):
                if a[x, y, z] == type_code:
                    coords.append((x, y, z))
    return coords
filter(a, FOO) # => [(1, 2, 3)]

numpy выглядит как хороший инструмент для того, чтобы делать то, что вы хотите, так как массивы будут меньше в памяти, легко доступны в C (или, что еще лучше, cython !) и расширенный синтаксис нарезки позволят вам не писать код.

person Luper Rouch    schedule 26.05.2009

Использование словаря с кортежами x, y, z в качестве ключей - еще одно решение, если вам нужно относительно простое решение со стандартной библиотекой.

import math

#use indexing to get point at (1,2,3): points[(1,2,3)]
get_points(points, x=None, y=None, x=None):
    """returns dict of all points with given x,y and/or z values.  Call using keywords (eg get_points(points, x=3)"""
    filteredPoints = points.items()
    if x:
        filteredPoints = [p for p in filteredPoints if p[0][0] == x]
    if y:
        filteredPoints = [p for p in filteredPoints if p[0][1] == y]
    if z:
        filteredPoints = [p for p in filteredPoints if p[0][0] == x]
    return dict(filteredPoints)

get_point_with_type(points, type_):
    """returns dict of points with point.getType() == type_"""
    filteredPoints = points.items()
    return dict((position,data) for position,data in points.iterItems() if data.getType == type_)

get_points_in_radius(points,x,y,z,r):
    """Returns a dict of points within radius r of point (x,y,z)"""
    def get_dist(x1,y1,z1,x2,y2,z3):
        return math.sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2)+(z1-z2)*(z1-z2))
    return dict((position,data) for position,data in points.iterItems() if get_dist(x,y,z, *position) <= r))

И из-за ссылки на python вы можете изменить «точки» в возвращаемых словарях, а также изменить исходные точки (я думаю).

person Community    schedule 26.05.2009

Когда использовать Binary Space Partitioning, Quadtree, Octree?

3D-массив imo бесполезен. Особенно, если ваш мир динамичен. Вы должны выбрать между BSP, Quadtree или Octtree. БСП вполне подойдет. Поскольку ваш мир находится в 3D, вам нужны плоскости при разделении bsp, а не линии.

Ваше здоровье !

Редактировать

У меня также будут данные для каждой точки в заданном трехмерном пространстве, поэтому я думаю, что запасная матрица — это плохо?

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

person ralphtheninja    schedule 26.05.2009

Это зависит от точной конфигурации вашей системы, но в приведенном вами примере вы используете целые числа и дискретные точки, поэтому, вероятно, было бы уместно рассмотреть Разреженная матрица структуры данных.

person Cruachan    schedule 26.05.2009