DaveL17 DaveL17 - 7 months ago 50
Python Question

Matplotlib Polar Plot with Lines

Trying to create a wind rose in Matplotlib using lines instead of bars. In other words, I would like something like this (which was created with Gnuplot) where the lines have a consistent thickness along the entire length and extend from the origin to the wind speed along the radial (the red is the most current observation, the indigo is peak, and the older obs get darker as they get older):

wind rose 1

The closest I have been able to come is this (please ignore any differences except for the lines):

wind rose 2

Everything that I've tried is plotted in a way that "fans out" from the origin--be it bars, lines, arrows, whatever. Here's the plot line:

bars = ax.bar(wind_direction, wind_speed, width=.075, linewidth=0.1, edgecolor='black', zorder=3)


SOLUTION:

Solution

Here is the complete code to create the desired plot (right above this line) thanks to @cphlewis' fix. My mistake when trying to use
plot
was to plot all the data in one command, rather than 'n' number of plots (plot each line individually.)

#! /usr/bin/env python2.6
# -*- coding: utf-8 -*-

import csv
import numpy as np
import matplotlib.pyplot as plt

bar_colors = ['#333333', '#444444', '#555555', '#666666', '#777777', '#888888', '#999999', 'red']
data_source = '/Users/username/Dropbox/Public/charts.csv'
num_obs = 8
final_data = []
wind_direction = []
wind_speed = []

# Get the data.
data_file = open(data_source, "r")
csv_data = csv.reader(data_file, delimiter=',')
[final_data.append(item) for item in csv_data]
data_file.close()

# Grab the column headings for the labels, then delete the row from final_data.
xlabel = final_data[0][24]
ylabel = final_data[0][25]
final_data.pop(0)

# Create lists of data to plot (string -> float).
[wind_direction.append(float(item[24])) for item in final_data]
[wind_speed.append(float(item[25])) for item in final_data]

# Make them the desired length based on num_obs.
wind_direction = wind_direction[len(wind_direction)-num_obs:len(wind_direction)]
wind_speed = wind_speed[len(wind_speed)-num_obs:len(wind_speed)]

# Polar plots are in radians (not degrees.)
wind_direction = np.radians(wind_direction)


wind = zip(wind_direction, wind_speed, bar_colors) # polar(theta,r)


# Customizations.
plt.figure(figsize=(3, 3)) # Size
ax = plt.subplot(111, polar=True) # Create subplot
plt.grid(color='#888888') # Color the grid
ax.set_theta_zero_location('N') # Set zero to North
ax.set_theta_direction(-1) # Reverse the rotation
ax.set_xticklabels(['N', 'NE', 'E', 'SE', 'S', 'SW', 'W', 'NW'], color='#666666', fontsize=8) # Customize the xtick labels
ax.spines['polar'].set_visible(False) # Show or hide the plot spine
ax.set_axis_bgcolor('#111111') # Color the background of the plot area.

# Create the plot.
# Note: zorder of the plot must be >2.01 for the plot to be above the grid (the grid defaults to z=2.)
for w in wind:
ax.plot((0, w[0]), (0, w[1]), color=w[2], linewidth=2, zorder=3)

# Right-size the grid (must be done after the plot), and customize the tick labels.
if max(wind_speed) <= 5:
ax.yaxis.set_ticks(np.arange(1, 5, 1))
ax.set_rgrids([1, 2, 3, 4, 5], angle=67, color='#FFFFFF', horizontalalignment='left', verticalalignment='center', fontsize=8)
elif 5 < max(wind_speed) <= 10:
ax.yaxis.set_ticks(np.arange(2, 10, 2))
ax.set_rgrids([2, 4, 6, 8, 10], angle=67, color='#FFFFFF', horizontalalignment='left', verticalalignment='center', fontsize=8)
elif 10 < max(wind_speed) <= 20:
ax.yaxis.set_ticks(np.arange(5, 20, 5))
ax.set_rgrids([5, 10, 15, 20], angle=67, color='#FFFFFF', horizontalalignment='left', verticalalignment='center', fontsize=8)
elif 20 < max(wind_speed) <= 50:
ax.yaxis.set_ticks(np.arange(10, 50, 10))
ax.set_rgrids([10, 20, 30, 40, 50], angle=67, color='#FFFFFF', horizontalalignment='left', verticalalignment='center', fontsize=8)
elif 50 < max(wind_speed):
plt.text(0.5, 0.5, u'Holy crap!', color='white', horizontalalignment='center', verticalalignment='center', transform=ax.transAxes, bbox=dict(facecolor='red', alpha=0.5))

# Plot circles for current obs and max wind.
fig = plt.gcf()
max_wind_circle = plt.Circle((0, 0), max(wind_speed), transform=ax.transData._b, fill=False, edgecolor='indigo', linewidth=2, alpha=1, zorder=9)
fig.gca().add_artist(max_wind_circle)
last_wind_circle = plt.Circle((0, 0), wind_speed[num_obs-1], transform=ax.transData._b, fill=False, edgecolor='red', linewidth=2, alpha=1, zorder=10)
fig.gca().add_artist(last_wind_circle)

# If latest obs is a speed of zero, plot something that we can see.
if wind_speed[num_obs-1] == 0:
zero_wind_circle = plt.Circle((0, 0), 0.1, transform=ax.transData._b, fill=False, edgecolor='red', alpha=1)
fig.gca().add_artist(zero_wind_circle)

# Save it to a file.
plt.savefig('/Users/username/Desktop/wind.png', facecolor='black', edgecolor='none')

Answer

Your real question is, how to plot on a polar plot with lines instead of bars? Answer: plot. A minimal and complete example of doing that:

#! /usr/bin/env python2.6
# -*- coding: utf-8 -*-

import csv
import numpy as np
import matplotlib.pyplot as plt

bar_colors     = ['#333333', '#444444', '#555555', '#666666', '#777777', '#888888', '#999999', '#AA0000']
num_obs        = len(bar_colors)

# Make up some data
wind_direction = (2*3.14)*(np.random.random_sample(num_obs))
wind_speed = 5 * np.random.random_sample(num_obs)
wind = zip(wind_direction, wind_speed, bar_colors) # polar(theta,r)

# Polar plotting
fig = plt.figure(figsize=(3, 3))  # Size
ax = plt.subplot(111, polar=True)  # Create subplot
plt.grid(color='#888888')  # Color the grid
ax.set_theta_zero_location('N')  # Set zero to North

for w in wind:
    #ax.plot(wind_speed, wind_direction, c = bar_colors, zorder = 3)
    ax.plot((0, w[0]), ( 0, w[1]), c = w[2], zorder = 3)

fig.show()

enter image description here