Creating a curved array diagram without contours

Question:

So I have a python array and a code that produces a 2D representation of it:

import numpy as np
import matplotlib.pyplot as plt

# Define the input data as a 2D NumPy array
arr = np.array([
    ['x', 'x', 'x', 'y', 'y', 'z', 'z', 'z', 'z', 'z', 'z'],
    ['x', 'x', 'x', 'y', 'y', 'z', 'z', 'z', 'z', 'z', 'z'],
    ['x', 'x', 'x', 'y', 'y', 'z', 'z', 'z', 'z', 'z', 'z'],
    ['x', 'x', 'x', 'y', 'y', 'z', 'z', 'z', 'z', 'z', 'z'],
    ['x', 'x', 'x', 'y', 'z', 'z', 'z', 'z', 'z', 'z', 'z'],
    ['x', 'x', 'x', 'x', 'z', 'z', 'z', 'z', 'z', 'z', 'z'],
    ['x', 'x', 'x', 'x', 'z', 'z', 'z', 'z', 'z', 'z', 'z'],
    ['x', 'x', 'x', 'x', 'x', 'z', 'z', 'z', 'z', 'z', 'z'],
    ['x', 'x', 'x', 'x', 'x', 'x', 'z', 'z', 'z', 'z', 'z'],
    ['x', 'x', 'x', 'x', 'x', 'x', 'z', 'z', 'z', 'z', 'z'],
])

# Convert input data to numerical values
num_arr = np.zeros(arr.shape)
num_arr[arr == 'x'] = 1
num_arr[arr == 'y'] = 2
num_arr[arr == 'z'] = 3

# Create a colormap
cmap = plt.get_cmap('viridis', 3)

# Plot the data
plt.imshow(num_arr, cmap=cmap)
plt.colorbar(ticks=[1, 2, 3], format=plt.FuncFormatter(lambda val, loc: {1: 'x', 2: 'y', 3: 'z'}[val]))
plt.title('Graphical Representation of Data')
plt.show()

This produces the following diagram:

enter image description here

I wanted to modify this diagram so that the edges between each colour look "curvier". Perhaps with a line of best fit between each region, or something like that. At the moment, due to the discreteness of the array, we have some very jagged-looking edges between each coloured region, instead I’d like the lines between each region to look more continuous and cleaner. In other words, I’m aiming for something more like this:

enter image description here

Everything I can find that could accomplish this is the contours function, or some equivalent. The problem is that if you use the contours function, it assumes there is a 2 between 1s and 3s, even when there isnt in the original data.

Is there another way to do this?

Asked By: Tom

||

Answers:

Here is a curved approach

import numpy as np
from scipy.interpolate import interp2d
import matplotlib.pyplot as plt
plt.figure(1,figsize=(12,4))

arr = np.array([
    ['x', 'x', 'x', 'y', 'y', 'z', 'z', 'z', 'z', 'z'],
    ['x', 'x', 'x', 'y', 'y', 'z', 'z', 'z', 'z', 'z'],
    ['x', 'x', 'x', 'y', 'y', 'z', 'z', 'z', 'z', 'z'],
    ['x', 'x', 'x', 'y', 'y', 'z', 'z', 'z', 'z', 'z'],
    ['x', 'x', 'x', 'y', 'z', 'z', 'z', 'z', 'z', 'z'],
    ['x', 'x', 'x', 'x', 'z', 'z', 'z', 'z', 'z', 'z'],
    ['x', 'x', 'x', 'x', 'z', 'z', 'z', 'z', 'z', 'z'],
    ['x', 'x', 'x', 'x', 'x', 'z', 'z', 'z', 'z', 'z'],
    ['x', 'x', 'x', 'x', 'x', 'x', 'z', 'z', 'z', 'z'],
    ['x', 'x', 'x', 'x', 'x', 'x', 'z', 'z', 'z', 'z'],
])

# Convert input data to numerical values
num_arr = np.zeros(arr.shape)
num_arr[arr == 'x'] = 1
num_arr[arr == 'y'] = 2
num_arr[arr == 'z'] = 3




BIG_SIDE = 100
big_colours = np.zeros((BIG_SIDE,BIG_SIDE))
big_arrays = {}
big_x = np.linspace(0, 9, BIG_SIDE)
big_y = np.linspace(0, 9, BIG_SIDE)

for color in ["x","y","z"]:
    arr_of_this_color = (arr == color )*1
    f = interp2d(np.arange(10), np.arange(10), arr_of_this_color, kind='cubic')
    big_array = f(big_x, big_y)
    big_arrays[color]=big_array

for x in range(BIG_SIDE):
    for y in range(BIG_SIDE):
        if big_arrays["x"][x][y]>= big_arrays["y"][x][y] and big_arrays["x"][x][y]>= big_arrays["z"][x][y]:
            big_colours[x][y]=1
        elif big_arrays["y"][x][y]>= big_arrays["z"][x][y]:
            big_colours[x][y]=2
        else:
            big_colours[x][y]=3
plt.subplot(1,3,1)
plt.imshow(big_colours, cmap=cmap)
plt.title('Cubic version')


for color in ["x","y","z"]:
    arr_of_this_color = (arr == color )*1
    f = interp2d(np.arange(10), np.arange(10), arr_of_this_color, kind='linear')
    big_array = f(big_x, big_y)
    big_arrays[color]=big_array

for x in range(BIG_SIDE):
    for y in range(BIG_SIDE):
        if big_arrays["x"][x][y]>= big_arrays["y"][x][y] and big_arrays["x"][x][y]>= big_arrays["z"][x][y]:
            big_colours[x][y]=1
        elif big_arrays["y"][x][y]>= big_arrays["z"][x][y]:
            big_colours[x][y]=2
        else:
            big_colours[x][y]=3
plt.subplot(1,3,2)
plt.imshow(big_colours, cmap=cmap)
plt.title('Linear version')


for color in ["x","y","z"]:
    arr_of_this_color = (arr == color )*1
    f = interp2d(np.arange(10), np.arange(10), arr_of_this_color, kind='quintic')
    big_array = f(big_x, big_y)
    big_arrays[color]=big_array

for x in range(BIG_SIDE):
    for y in range(BIG_SIDE):
        if big_arrays["x"][x][y]>= big_arrays["y"][x][y] and big_arrays["x"][x][y]>= big_arrays["z"][x][y]:
            big_colours[x][y]=1
        elif big_arrays["y"][x][y]>= big_arrays["z"][x][y]:
            big_colours[x][y]=2
        else:
            big_colours[x][y]=3

plt.subplot(1,3,3)
plt.imshow(big_colours, cmap=cmap)
plt.title('Quintic version')
plt.show()

Of these results, the "linear" is, ironically, the least weird-looking.

enter image description here

Answered By: ProfDFrancis