Plot RGB map from three columns of data using Matplotlib - python

Is it possible to plot a map of RGB values using Matplotlib?
I have three columns of data that I read from a text file in the following form where x and y are the desired coordinates and z is a hex string of the desired rgb color to plot at the given coordinates:
x y z
1 0.5 #000000
2 0.5 #FF0000
3 0.5 #00FF00
1 1.5 #0000FF
2 1.5 #FFFF00
3 1.5 #00FFFF
1 1.5 #FF00FF
2 2.5 #C0C0C0
3 2.5 #FFFFFF
This is my current state of code. An error is thrown from the griddata() function:
import pandas as pds
import matplotlib.pyplot as plt
# Import text file using pandas
datafile = pds.read_csv(PathToData,sep='\t')
X=datafile.x
Y=datafile.y
Z=datafile.z
# Generate mesh as 'numpy.ndarray' type for plotting
# This throws the following error:
# ValueError: could not convert string to float: #FFAA39
Z=griddata(X, Y, Z, unique(X), unique(Y))
Many thanks

griddata is a function for interpolating unevenly spaced data on to a grid (griddata doc). In your case, it looks like you already have data on a grid, you just need to reshape it. The error you are getting arises because griddata is trying to convert you color hex codes to floats for the interpolation, which you should be getting because there is not a sensible float interpertation of #00FF00.
data_dims = (3, 3) # or what ever your data really is
X = np.asarray(x).reshape(data_dims)
Y = np.asarray(y).reshape(data_dims)
C = np.asarray([mpl.colors.colorConverter.to_rgb(_hz) for _hz in z]).reshape(data_dims + (3,))
# the colors converted to (r, g, b) tuples
The asarray calls are to make sure we have arrays, not data frames.
If you just want to see the array we can use imshow to plot it:
imshow(c, interpolation='none',
origin='bottom',
extent=[np.min(X), np.max(X), np.min(Y), np.max(Y)])
color converter doc, imshow doc.
You might need to transpose/flip/rotate C to get the orientation you expect.

Related

Matplotlib: Using strings as values instead of numbers

My data is a 100 x 100 array of strings which are all hex codes:
[['#10060e' '#11070f' '#060409' ... '#08030a' '#09030d' '#12050f']
['#110600' '#09010e' '#0d0210' ... '#09030f' '#08060b' '#160a0a']
['#0a070e' '#13060f' '#0c040f' ... '#0c0610' '#0e040c' '#0a020f']
...
['#0c020d' '#09040b' '#10070c' ... '#0a090f' '#160613' '#08000f']
['#0a020f' '#09040a' '#150812' ... '#11040d' '#07040b' '#0b060d']
['#0d0715' '#0e020c' '#140710' ... '#0a0112' '#12090e' '#0c020d']]
Matplotlib: throws this error: TypeError: cannot perform reduce with flexible type
I think the issue it is having is it cannot give a colour to these as they are strings, not numbers. I can only find examples where all the data is numerical and has a colour map applied to it, nothing where every bit of data's colour is specified.
I would like to tell Matplotlib what colour I'd like all of these to be using, surprise surprise, the hex codes. How can I go about doing that?
Full(er) code sample:
z = np.asanyarray(pixel_arr)
x = np.arange(0, width, 1) # len = 100
y = np.arange(0, height, 1) # len = 100
fig, ax = plt.subplots()
ax.pcolormesh(x, y, z)
plt.show()
I would like to tell Matplotlib what colour I'd like all of these to be using
It sounds like you have a bunch of pixel values you want to plot, i.e., an image. So you can treat it like one.
import matplotlib.pyplot as plt
import numpy as np
data = np.array([['#10060e', '#11070f', '#060409', '#08030a', '#09030d', '#12050f'],
['#110600', '#09010e', '#0d0210', '#09030f', '#08060b', '#160a0a'],
['#0a070e', '#13060f', '#0c040f', '#0c0610', '#0e040c', '#0a020f'],
['#0c020d', '#09040b', '#10070c', '#0a090f', '#160613', '#08000f'],
['#0a020f', '#09040a', '#150812', '#11040d', '#07040b', '#0b060d'],
['#0d0715', '#0e020c', '#140710', '#0a0112', '#12090e', '#0c020d']])
img = [[tuple(bytes.fromhex(pixel[1:])) for pixel in row] for row in data]
img = np.array(img, dtype=np.uint8)
plt.imshow(img)
plt.show()
The output may be dark at first glance, but that's because I used the data you showed us, which all happen to be very dark pixels.

wrong number of arguments when using x, y arrays as pixel positions and z as colour value in matplotlib.pcolor()

I have been trying to make a coloured image with a colourbar scale. Each pixel should correspond to colour bar values from the Z array, and x,y should be pixel position arguments (in mm) similar to this:
https://ars.els-cdn.com/content/image/1-s2.0-S0010218014001989-gr6_lrg.jpg
Data is imported across many text files. However, a small example section of this input data can be recreated from the following code file to pandas dataframe code, (where the x axis range is -30 - 30mm (increment by 1) and y axis range is 6 - 15 mm (increment by 3):
import numpy as np
import pandas as pd
x = np.linspace(-30,30,61)
y = np.linspace(6, 15, 4)
z = 1.35 * np.random.rand(61*4, 1)
for i in range(0,3,1):
x = np.append(x,x[:61])
y = np.repeat(y,61)
df = pd.DataFrame()
df['X [mm]'] = x
df ['Y [mm]'] = y
df['LDA1-Mean [m/s]'] = z
print(df)
Now running the following code:
import matplotlib.pyplot as plt
Z = df['LDA1-Mean [m/s]'].to_list()
positions = np.array(list(zip(df['X [mm]'], df['Y [mm]'])))
plt.pcolor(positions, Z)
plt.show()
plt.savefig('solutions/graphs/test.png', dpi=300, bbox_inches="tight")
Produces the following error:
TypeError: pcolor() takes 1 or 3 positional arguments but 2 were given
Is there a better way to do this with imshow() or contourf(), I'm open to suggestions.
As the order of X and Y data cannot as consistent as, I'd prefer to take always use the X and Y position data rather than reordering Z data (and using only 1 argument).
Thank you in advance for the help - I am still new to programming. Please feel free to ask questions if there is something I have not explained.

How to plot in 3D with a double entry table - Matplotlib

I would like to plot in 3D with Pandas / MatplotLib (Wireframe or other, I do not care) but in a specific way..
I'm using RFID sensors and I'm trying to record the signal I receive at different distance + different angles. And I want to see the correlation between the rising of the distance and the angle.
So that's why I want to plot in 3D :
X Axis -> the Distance, Y Axis -> the Angle, Z Axis -> the signal received which means a float
My CSV file from where I generate my DataFrame is organized like this a double entry table :
Distance;0;23;45;90;120;180
0;-53.145;-53.08;-53.1;-53.035;-53.035;-53.035
5;-53.145;-53.145;-53.05;-53.145;-53.145;-53.145
15;-53.145;-53.145;-53.145;-53.145;-53.145;-53.145
25;-53.145;-52.145;-53.145;-53.002;-53.145;-53.145
40;-53.145;-53.002;-51.145;-53.145;-54.255;-53.145
60;-53.145;-53.145;-53.145;-53.145;-53.145;-53.145
80;-53.145;-53.145;-53.145;-53.145;-60;-53.145
100;-53.145;-52;-53.145;-54;-53.145;-53.145
120;-53.145;-53.145;-53.145;-53.145;-53.002;-53.145
140;-51.754;-53.145;-51.845;-53.145;-53.145;-53.145
160;-53.145;-53.145;-49;-53.145;-53.145;-53.145
180;-53.145;-53.145;-53.145;-53.145;-53.145;-53.002
200;-53.145;-53.145;-53.145;-53.145;-53.145;-53.145
On the first label row we've different angles : 0°, 23°, 45°, ...
And the index of the DataFrame is the distance : 0 cm, 15 cm...
And the matrix inside represents the signal, so, values of Z Axis...
But I do not know how to generate a 3D Scatter, WireFrame... because in every tutorial I see people that use specific columns as axis.
Indeed, in my CSV file on the first row I've the label of all columns
Distance;0 ;23 ;45 ;90 ;120;180
And I do not know how to generate a 3D plot with a double entry table.
Do you know how to do it ? Or, to generate my CSV file in a better way to see the same result at the end !
I would be grateful if you would help me about this !
Thank you !
maybe contour is enough
b = np.array([0,5,15,25,40,60,80,100,120,140,160,180,200])
a = np.array([0,23,45,90,120,180])
x, y = np.meshgrid(a, b)
z = np.random.randint(-50,-40, (x.shape))
scm = plt.contourf(x, y, z, cmap='inferno')
plt.colorbar(scm)
plt.xticks(a)
plt.yticks(b)
plt.xlabel('Distance')
plt.ylabel('Angle')
plt.show()
displays
You can get a contour plot with something like this (but for the data shown it is not very interesting since all the values are constant at -45):
df = pd.read_csv(sep=';')
df = df.set_index('Distance')
x = df.index
y = df.columns.astype(int)
z = df.values
X,Y = np.meshgrid(x,y)
Z = z.T
plt.contourf(X,Y,Z,cmap='jet')
plt.colorbar()
plt.show()
Welcome to stackoverflow, your question can be split into several steps:
Step 1 - read the data
I have stored your data in a file called data.txt.
I don't know Pandas very well but this can also be handled with the nice simple function of Numpy called loadtxt. Your data is a bit problematic because of the text 'Distance' value in the first column and first row. But don't panic we load the file as a matrix of strings:
raw_data = np.loadtxt('data.txt', delimiter=';', dtype=np.string_)
Step 2 - transform the raw data
To extract the wanted data from the raw data we can do the following:
angle = raw_data[0 , 1:].astype(float)
distance = raw_data[1:, 0 ].astype(float)
data = raw_data[1:, 1:].astype(float)
With indexing the raw data we select the data that we want and with astype we change the string values to numbers.
Intermediate step - making the data a bit fancier
Your data was a bit boring, only the value -45, i took the liberty to make it a bit fancier:
data = (50 + angle[np.newaxis,:]) / (10 + np.sqrt(distance[:,np.newaxis]))
Step 4 - make a wireframe plot
The example at matplotlib.org looks easy enough:
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.plot_wireframe(X, Y, Z)
plt.show()
But the trick is to get the X, Y, Z parameters right...
Step 3 - make the X and Y data
The Z data is simply our data values:
Z = data
The X and Y should also be 2D array's such that plot_wireframe can find the x and y for each value of Z in the 2D arrays X an Y at the same array locations. There is a Numpy function to create these 2D array's:
X, Y = np.meshgrid(angle, distance)
Step 5 - fancing it up a bit
ax.set_xticks(angle)
ax.set_yticks(distance[::2])
ax.set_xlabel('angle')
ax.set_ylabel('distance')
Putting it together
All steps together in the right order:
# necessary includes...
from mpl_toolkits.mplot3d import axes3d
import matplotlib.pyplot as plt
import numpy as np
raw_data = np.loadtxt('data.txt', delimiter=';', dtype=np.string_)
angle = raw_data[0 , 1:].astype(float)
distance = raw_data[1:, 0 ].astype(float)
data = raw_data[1:, 1:].astype(float)
# make the example data a bit more interesting...
data = (50 + angle[np.newaxis,:]) / (10 + np.sqrt(distance[:,np.newaxis]))
# setting up the plot
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
# the trickey part creating the data that plot_wireframe wants
Z = data
X, Y = np.meshgrid(angle, distance)
ax.plot_wireframe(X, Y, Z)
# fancing it up a bit
ax.set_xticks(angle)
ax.set_yticks(distance[::2])
ax.set_xlabel('angle')
ax.set_ylabel('distance')
# and showing the plot ...
plt.show()

Custom coloration for matrix in matplotlib

I have three matrices that I'd like to plot, but the only solution I've come up with is just plotting one after the other, and that leaves me with the last matrix plotted.
ax.imshow(mat1, cmap='Blues', interpolation='nearest')
ax.imshow(mat2, cmap='binary', interpolation='nearest')
ax.imshow(mat3, cmap='autumn', interpolation='nearest') # actual plot
What I want is to display all 0s in the three matrices in white, and higher values in different tones depending on the matrix, e.g.: blue, black and red. Also, in that example, red cells would have precedence over black and black over blue. The solution I'm imagining to this is a function that, given a triple (blue, black, red) with the different values for each component, returns the color the cell should be colored, and feed it to a ColorMap, but I really don't know how to do so or if it's even possible.
Every kind of help and even different solutions (that's the most likely to happen) are welcome and appreciated. Thanks in advance.
You want a fourth image, with the RGB value at each point a function of the single value of the first three matrixes at the corresponding point? If so, can you produce the algebra to get from three values to the RGB fourth?
Your question suggests confusion about how plotting turns data into colors. A colormap takes single-valued data, normalizes it, and maps it into some named array of colors. 0 values might be mapped to any color, depending on the colormap and the rest of the data.
A bitmap defines (red, green, blue) values at each pixel. Proper bitmaps have header sections, but the data is an (m,n,3) array. imshow plots just that array; it expects the RGB values to be in the [0,1] range.
If you have three data matrices, you have to choose how to map the values to RGB values. Here's an example with three kinds of mapping to RGB. The first two rows are dummy data with a range of values, shown either with a colormap or as the simplest RGB representation. The last row shows ways of combining all three dummy matrices into one image using the whole colorspace.
# -*- coding: utf-8 -*-
import matplotlib.pyplot as plt
import numpy as np
#dummy data
x = 8
y = 15
mat = []
mat.append(np.arange(x * y).reshape((x, y)) / float(x * y) )
mat.append(np.arange(x * y).reshape((y, x)).T / float(x* y))
mat.append(np.arange(y) * np.arange(x)[:,np.newaxis] / float(99))
# NOTE: this data is approximately in the RGB range. If yours isn't, normalize,
# here or in your makeRGB function.
# (The colormap normalizes single-valued data).
fig, axs = plt.subplots(figsize=(7,4), nrows=3, ncols=3,
gridspec_kw={'hspace':0.6})
axs[0,0].imshow(mat[0], cmap='Reds', interpolation='nearest')
axs[0,1].imshow(mat[1], cmap='Greens', interpolation='nearest')
axs[0,2].imshow(mat[2], cmap='Blues', interpolation='nearest')
axs[0,0].set_xlabel('Reds Colormap')
axs[0,1].set_xlabel('Greens Colormap')
axs[0,2].set_xlabel('Blues Colormap')
def asOneHue(mat, hue):
"""
Use a single-valued matrix to represent one hue in a RGB file.'
"""
RGBout = np.zeros((len(mat),len(mat[0]),3))
RGBout[:,:,i] = mat
return RGBout
for i in (0,1,2):
axs[1,i].imshow(asOneHue(mat[i],i))
axs[1,0].set_xlabel('Reds bitmap')
axs[1,1].set_xlabel('Greens bitmap')
axs[1,2].set_xlabel('Blues bitmap')
# different ways to combine 3 values
def makeRGB0(mats):
RGBout = np.zeros((len(mats[0]),len(mats[0][0]),3))
#RGBout = np.ones((len(mats[0]),len(mats[0][0]),3))
for i in (0,1,2):
RGBout[:,:,i] = mats[i]
return RGBout
axs[2,0].imshow(makeRGB0(mat))
axs[2,0].set_xlabel('Color layers')
def makeRGB1(mats):
RGBout = np.zeros((len(mats[0]),len(mats[0][0]),3))
i,j,k = RGBout.shape
for x in range(i):
for y in range(j):
RGBout[x,y] = (mats[0][x][y] / 2,
mats[1][x][y],
1 - mats[2][x][y])
return RGBout
axs[2,1].imshow(makeRGB1(mat))
axs[2,1].set_xlabel('Algebraic')
def makeRGB2(mats):
RGBout = np.zeros((len(mats[0]),len(mats[0][0]),3))
i,j,k = RGBout.shape
for x in range(i):
for y in range(j):
if mats[0][x][y] > .8:
RGBout[x,y] = (mats[0][x][y],
0,
0)
elif mats[1][x][y] > .8:
RGBout[x,y] = (0,
mats[1][x][y],
0)
else:
RGBout[x,y] = (mats[0][x][y],
mats[1][x][y],
mats[2][x][y])
return RGBout
axs[2,2].imshow(makeRGB2(mat))
axs[2,2].set_xlabel('If-else')
plt.show()

Use matplotlib.contour with complex data

I'm trying to show a contour plot using matplotlib from a complex array. The array is a 2x2 complex matrix, generated by the (C like) method:
for i in max_y:
for j in max_x:
pos_x = pos_x + step
z = complex(pos_x,pos_y)
c_arr[i][j] = complex_function(z)
pos_y = pos_y + step
I would like to plot this c_arr (real part) using contourplot, but so far the only thing that I can get from contour is
TypeError: Input z must be a 2D array.
The c_arr.real is a 2D array, and doesn't matter if I make a grid with x, y, or pos_x, or pos_y, the result is always the same. The docs from matplotlib tells me how to use it, but not the datatypes necessary to use it, so I feel left in the dark.
EDIT: Thanks for the answer. My problem now is that I have to get the complex values from a function in this form:
def f(z):
return np.sum(np.arange(n)*np.sqrt(z-1)**np.arange(n))
where the sum must be added up. How can this be accomplished using the meshgrid form that contour needs? Thanks again.
matplotlib.pyplot.contour() allows complex-valued input arrays. It extracts real values from the array implicitly:
#!/usr/bin/env python
import numpy as np
from matplotlib import pyplot as plt
# generate data
x = np.r_[0:100:30j]
y = np.r_[0:1:20j]
X, Y = np.meshgrid(x, y)
Z = X*np.exp(1j*Y) # some arbitrary complex data
# plot it
def plotit(z, title):
plt.figure()
cs = plt.contour(X,Y,z) # contour() accepts complex values
plt.clabel(cs, inline=1, fontsize=10) # add labels to contours
plt.title(title)
plt.savefig(title+'.png')
plotit(Z, 'real')
plotit(Z.real, 'explicit real')
plotit(Z.imag, 'imagenary')
plt.show()
real
explicit real
imagenary

Categories

Resources