Reorganize os dados em um array bidimensional de acordo com a transformação de coordenadas polares para cartesianas

8

Eu tenho uma matriz bidimensional que representa valores de função em posições em um sistema de coordenadas polares. Por exemplo:

import numpy as np

radius = np.linspace(0, 1, 50)
angle = np.linspace(0, 2*np.pi, radius.size)
r_grid, a_grid = np.meshgrid(radius, angle)
data = np.sqrt((r_grid/radius.max())**2
               + (a_grid/angle.max())**2)

Aqui o dataé organizado em uma grade retangular correspondente às coordenadas polares. Quero reorganizar os dados na matriz, de modo que os eixos representem o sistema de coordenadas cartesianas correspondente. O layout antigo versus o novo pode ser visualizado da seguinte maneira:

import matplotlib.pyplot as plt

fig, (ax1, ax2) = plt.subplots(ncols=2, figsize=plt.figaspect(0.5))
ax1.set(title='Polar coordinates', xlabel='Radius', ylabel='Angle')
ax1.pcolormesh(r_grid, a_grid, data)
ax2.set(title='Cartesian coordinates', xlabel='X', ylabel='Y')
x_grid = r_grid * np.cos(a_grid)
y_grid = r_grid * np.sin(a_grid)
ax2.pcolormesh(x_grid, y_grid, data)

Exemplo

Aqui as coordenadas são explicitamente dadas e o gráfico é ajustado de acordo. Quero que os dados sejam reorganizados na própria matriz de dados. Ele deve conter todos os valores, opcionalmente preenchendo com zeros para ajustar a forma (semelhante a scipy.ndimage.rotate(..., reshape=True)).

Se eu fizer um loop manual sobre as matrizes polares para calcular as coordenadas cartesianas, o resultado conterá regiões vazias que, idealmente, devem ser preenchidas também:

new = np.zeros_like(data)
visits = np.zeros_like(new)
for r, a, d in np.nditer((r_grid, a_grid, data)):
    i = 0.5 * (1 + r * np.sin(a)) * new.shape[0]
    j = 0.5 * (1 + r * np.cos(a)) * new.shape[1]
    i = min(int(i), new.shape[0] - 1)
    j = min(int(j), new.shape[1] - 1)
    new[i, j] += d
    visits[i, j] += 1
new /= np.maximum(visits, 1)
ax2.imshow(new, origin='lower')

Exemplo de tentativa

Existe uma maneira de conseguir a transformação, evitando regiões vazias na matriz de dados resultante?

um convidado
fonte

Respostas:

2

tl; dr: Não, não sem alterar algumas condições do seu problema.

O artefato que você está vendo é uma propriedade da transformação. Isso não se deve à resolução fixa em ângulo para todos os raios. Portanto, não é devido a uma implementação incorreta ou ruim da transformação. A grade cartesiana implica simplesmente uma resolução especial mais alta nessas áreas, pois existem pontos resolvidos no mapa polar.

  • A única maneira "limpa" (em que posso pensar agora) de lidar com isso é ter uma resolução ajustável nas coordenadas polares para dar conta da escala de 1 / r. (Se você inserir dados permite)

  • Uma maneira um tanto enganadora de visualizar isso sem as lacunas seria distribuí-las aleatoriamente sobre as lacunas. O argumento aqui é que você não tem a resolução para decidir em qual posição eles deveriam começar. Então, você pode jogá-los aleatoriamente em uma que possa ter sido uma origem possível e não jogá-los todos na mesma (como você está fazendo agora). No entanto, eu gostaria de desencorajar esse stronlgy. Apenas oferece uma trama mais bonita. Observe que isso é um pouco equivalente ao comportamento do gráfico superior direito na sua pergunta.

465b
fonte
De fato, o problema é diminuir a resolução espacial com raio crescente, já que a resolução angular é fixa. Eu apenas tentei uma abordagem em que os dados angulares são interpolados para cada raio com densidade proporcional a 1/rantes da transformação em coordenadas cartesianas ser computada. Os resultados parecem promissores, y = 0restam apenas alguns artefatos , então ainda estou analisando.
a_guest 10/03
1

Isso realmente não dá o resultado esperado, mas talvez o ajude a obter uma solução após algumas correções necessárias ...


import numpy as np

radius = np.linspace(0, 1, 50)
angle = np.linspace(0, 2*np.pi, radius.size)
r_grid, a_grid = np.meshgrid(radius, angle)
data = np.sqrt((r_grid/radius.max())**2
               + (a_grid/angle.max())**2)


def polar_to_cartesian(data):
    new = np.zeros_like(data) * np.nan
    x = np.linspace(-1, 1, new.shape[1])
    y = np.linspace(-1, 1, new.shape[0])
    for i in range(new.shape[0]):
        for j in range(new.shape[1]):
            x0, y0 = x[j], y[i]
            r, a = np.sqrt(x0**2 + y0**2), np.arctan2(y0, x0)
            data_i = np.argmin(np.abs(a_grid[:, 0] - a))
            data_j = np.argmin(np.abs(r_grid[0, :] - r))
            val = data[data_i, data_j]

            if r <= 1:
                new[i, j] = val

    return new

new = polar_to_cartesian(data)
fig, ax = plt.subplots()
ax.imshow(new, origin='lower')

insira a descrição da imagem aqui

EDIT: Modificado usando de np.arctan2acordo com as sugestões do OP.

dzang
fonte
Para obter a dependência angular correta, np.arctan2deve-se usar, mas, de qualquer forma, isso introduz enormes discrepâncias em relação às bordas do sistema de coordenadas cartesianas. Na realidade, não há pontos de dados, mas como essa abordagem considera apenas o ponto de dados disponível mais próximo, ele é preenchido enquanto não deveria.
a_guest 10/03
@a_guest na verdade, apenas considera o valor mais próximo. É basicamente fazendo uma interpolação de vizinho mais próximo. Se você precisar de uma interpolação mais avançada, acho que fica muito mais complicado, pois a interpolação deve ser feita no espaço radial. Talvez seja uma possibilidade de usar skimage.transform.resizepara aumentar a amostra da imagem polar por um determinado fator e usar uma interpolação bilinear o bicúbica e, em seguida, usar essa abordagem para fazer a transformação. Você terminaria com uma transformação mais precisa.
dzang 10/03
@a_guest limitar o raio a 1 poderia resolver os problemas de fronteiras?
dzang 10/03
0

Você pode fazer um loop na matriz cartesiana, transformando cada ponto da grade em coordenadas polares e aproximando o valor da função por interpolação a partir dos dados da grade polar. Você ainda pode deixar as regiões de canto em branco, por falta de dados suficientes.

Eu não acho que exista uma maneira melhor, a menos que você tenha acesso à função original.

Arne
fonte
Se bem entendi, esses pontos de grade interpolados estarão na posição "entre" os pontos de grade atuais. Portanto, é indecidível de qual ponto de grade eles vieram. Ou eu entendi mal você?
465b 10/03
Sim, quando você transforma um dos pontos da grade cartesiana em coordenadas polares, ele geralmente fica entre quatro dos pontos da grade polar. Portanto, você atribui a ele um valor de função calculando uma média dos valores de função desses quatro pontos de grade, ponderados de acordo com a distância. Isso é o que eu quis dizer com interpolação.
Arne
Ah tudo bem. Mas isso apenas torna os pontos individuais "maiores", pois os distribui por quatro pontos, o que ajuda apenas a valores baixos de r. Isso ainda resultará em um padrão em forma de estrela para valores maiores de r
465b
Não, eu não diria isso. Com o método descrito acima, você pode calcular uma estimativa distinta do valor da função para cada ponto da grade cartesiana.
Arne