Is there any easy way to plot all features in a GeoDataFrame as the same color, rather than the default color map?
Say I have the following geodataframe of linestrings:
>>> import geopandas as gpd
>>> from shapely.geometry import LineString
>>>
>>> gdf=gpd.GeoDataFrame(geometry=[LineString([(1,2),(4,5)]),LineString([(6,3),(7,3)]),LineString([(6,2),(8,9)])])
>>> gdf
geometry
0 LINESTRING (1 2, 4 5)
1 LINESTRING (6 3, 7 3)
2 LINESTRING (6 2, 8 9)
>>>
How can I use gdf.plot() but have all 3 LineStrings show up colored black?
Since setting a single color is not yet implemented, you can simply create your own colormap with only one color.
from matplotlib.colors import ListedColormap
mycolor = ListedColormap('blue')
For your example this would lead to
import geopandas as gpd
from shapely.geometry import LineString
from matplotlib.colors import ListedColormap
mycolor = ListedColormap('blue')
gdf=gpd.GeoDataFrame(geometry=[LineString([(1,2),(4,5)]),LineString([(6,3),(7,3)]),LineString([(6,2),(8,9)])])
gdf.plot(colormap=mycolor)
Figure Result
Of course you can use any other matplotlib color.
Related
I'm trying to create a Cartopy map for the sub-polar region around Iceland. What I would like is a non-rectangular figure where the edges are parallel to the lines of longitude and latitude, like this figure created using PyGMT:
I've tried various Cartopy projections, but all result in a rectangular figure, e.g.,
import matplotlib.pyplot as plt
import cartopy
import cartopy.crs as ccrs
fig = plt.figure()
proj = ccrs.LambertConformal(central_longitude=-25, central_latitude=58.0)
ax = plt.axes(projection = proj)
ax.set_extent((-45, -5, 45, 70))
ax.gridlines()
ax.add_feature(cartopy.feature.LAND, zorder=1, edgecolor='black')
There are reasons for not using PyGMT (I want to plot surface velocities using quiver, plus the extensive learning curve), so I'd like to know if it's possible to achieve the same result in cartopy.
Thanks
You can use the set_boundary method of an axes for this. When specifying it as lon/lat, for a different projection, you should sample some points accross the boundary to approximate the true curvature of the projection (compared to lon/lat). The example below takes 20 points on each edge.
Note that the shape of this boundary can be anything you want, it doesn't have to match the projection or lon/lat lines etc.
import matplotlib.pyplot as plt
import matplotlib.path as mpath
import cartopy
import cartopy.crs as ccrs
import numpy as np
proj = ccrs.LambertConformal(central_longitude=-25, central_latitude=58.0)
fig, axs = plt.subplots(
1,2, figsize=(8, 3), facecolor="w",
subplot_kw=dict(projection=proj),
)
n = 20
aoi = mpath.Path(
list(zip(np.linspace(-45,-5, n), np.full(n, 70))) + \
list(zip(np.full(n, -5), np.linspace(70, 45, n))) + \
list(zip(np.linspace(-5, -45, n), np.full(n, 45))) + \
list(zip(np.full(n, -45), np.linspace(45, 70, n)))
)
axs[1].set_boundary(aoi, transform=ccrs.PlateCarree())
for ax in axs:
ax.set_extent((-45, -5, 45, 70))
ax.add_feature(cartopy.feature.LAND, zorder=1, edgecolor='k')
gl = ax.gridlines(
draw_labels=True, rotate_labels=False,
x_inline=False, y_inline=False,
)
The answer by #Rutger_Kassies is great. However there is an alternative that the readers should consider if he/she wants to try a different approach.
import cartopy.crs as ccrs
import cartopy
import matplotlib.pyplot as plt
import matplotlib.path as mpath
# The lat-long proj
noProj = ccrs.PlateCarree()
# The projection of the map
myProj = ccrs.LambertConformal(central_longitude=-25, central_latitude=58.0)
myProj._threshold = myProj._threshold/20. # Set for higher precision of the projection
ax = plt.axes(projection=myProj)
# This plots parallel and meridian arcs around a target area that will be used ...
# as the map boundary
[ax_hdl] = ax.plot([-45, -5, -5, -45, -45], [45, 45, 70, 70, 45],
color='black', linewidth=0.5, marker='none',
transform=noProj)
# Get the `Path` of the plot
tx_path = ax_hdl._get_transformed_path()
path_in_data_coords, _ = tx_path.get_transformed_path_and_affine()
# Use the path's vertices to create a polygon
polygon = mpath.Path( path_in_data_coords.vertices )
ax.set_boundary(polygon) #This masks-out unwanted part of the plot
ax.gridlines(draw_labels=True, x_inline=False, y_inline=False)
ax.add_feature(cartopy.feature.OCEAN, linewidth=.3, color='lightblue')
ax.add_feature(cartopy.feature.LAND, zorder=1, edgecolor='black')
ax.title.set_text("Meridians and Parallels as Boundary")
plt.show()
You can change some parameters in the code, for example, the type of arcs that are used as the map's boundary.
The second plot is obtained by changing these parts of code:
1. `transform=noProj` to
`transform=ccrs.Geodetic()`
2. `ax.title.set_text("Meridians and Parallels as Boundary")` to
`ax.title.set_text("Great-circle Arcs as Boundary")`
I believe that when the top edge of the map reaches high latitude, using parallel of latitude as the boundary is not optimum. Straight line may be better, but in some situation great circle arc should be considered.
I have a dataframe with some columns. I've created a Networkx undirected graph and I want to draw the corresponding network. I need also to change the color of the edges based on the weights of those edges, so I used a LinearColorMap. This is the code:
#my other stuff
cmap = LinearSegmentedColormap.from_list('RwG',['red','white','green'])
nx.draw(G, poss, node_size=1500, node_color='lightgrey', edgelist=edges,edge_color=weights,
width=list(map(lambda number: number * 16 / m, x)), edge_cmap=cmap)
However, I need to normalize my color map so that the white color is centered on a specific value (e.g. -76). The weights are in the [-60,-100] range.
How can I achieve that ?
Visually:
If you pass in a matplotlib colormap to networkx, networkx will normalize your numerical color argument linearly between the minimum and maximum value.
Personally, I think this is a somewhat shortsighted design decision but it is what it is: your non-linear mapping of weights to color is simply not possible.
You can, however, pre-compute the colors and pass those in instead (similarly how you are precomputing edge widths):
#!/usr/bin/env python
import numpy as np
import matplotlib.pyplot as plt
import networkx as nx
def weight_to_color(w, threshold):
if w < threshold:
return 'green'
elif np.isclose(w , threshold):
return 'white'
else: # x > threshold
return 'red'
weight_matrix = 40 * np.random.rand(10, 10) - 100
g = nx.from_numpy_array(weight_matrix)
weights = [g.edges[edge]['weight'] for edge in g.edges]
nx.draw(g, node_color='lightgray', edgelist=g.edges, edge_color=[weight_to_color(w, -76) for w in weights])
plt.show()
I am wondering whether it is possible to plot a vertical bar over a 2-dimensional representation of a graph. Say I have a tree and I want to associate with any node a "potential" which can be represented as a vertical bar.
NetworkX can do that using the matplotlib drawing tools because the result is a matplotlib figure on which you can use matplotlib to draw anything else you'd like on top of the networkx drawing of the graph.
nx.draw(G)
mpl.plot([xpt, xpt], [ymin, ymax], '--b')
mpl.show()
This is a minimal example which does what I was looking for (in Python):
import networkx as nx
import random
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import numpy as np
degree = 3
N = 10
g = nx.random_regular_graph(degree, N)
fig = plt.figure(figsize=(10,7))
ax = Axes3D(fig)
for i,j in enumerate(g.edges()):
x = np.array((positions[j[0]][0], positions[j[1]][0]))
y = np.array((positions[j[0]][1], positions[j[1]][1]))
ax.plot(x, y, c='black', alpha=0.5)
for key, value in positions.items():
xi = value[0]
yi = value[1]
# Scatter plot
ax.scatter(xi, yi, c= 'red')
ax.bar3d(xi, yi, 0, 0.01, 0, random.random(), shade=False)
ax.set_axis_off()
It generates this kind of plot, which can be useful to represent additional information on a graph
I have a list containing the names of different methods and their performance on a test set which I want to show using a bar chart. Well, in fact I would like to draw their relative improvement/degradation with respect to the baseline model. So, the data looks like:
system_1,+2.5
system_2,-0.8
system_3,+0.24
I've tried the bar chart in seaborn which gives me a simple bar chart with a fixed color. But, what I am looking for a bar chart in which the colours are in the range of red, white, green where the red corresponds to data['score'].min(), white corresponds to 0 and green represents the data['score'].max(). I would like the darkness/brightness of the colours show their distance from 0 meaning that dark red shows the worst system and dark green shows the best performing system and all the performances in the middle being shown by lighter colours.
I've found some solutions to make gradient colours, but they don't do what I expect. Here is my code and the chart that I get.
import seaborn as sns
import pandas as pd
import matplotlib.pyplot as plt
import sys
import numpy as np
sns.set(style="whitegrid", color_codes=True)
data = pd.read_csv(sys.argv[1])
pal = sns.color_palette("Greens_d", len(data))
colors = [0 if c >=0 else 1 for c in data['performance']]
ax = sns.barplot(x="performance", y="System", data=data, palette=pal)
plt.tight_layout()
plt.show()
As you see, instead of making the color range depending on the value of the datapoints it varies the colors based on the indexes of the data points. Do you have any idea on this?
Thank you very much!
The following approach uses a diverging norm and the red-yellow-green colormap to map the lowest value to the red extreme, zero to yellow and the highest to green.
As the short bars get a very light color, a black edge is added to make every bar clearly visible.
import seaborn as sns
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib.colors import DivergingNorm
import numpy as np
sns.set(style='whitegrid', color_codes=True)
N = 11
data = pd.DataFrame({'System': [f'System {i}' for i in range(1, N + 1)],
'performance': np.random.uniform(-1.5, 2.5, N)})
norm = DivergingNorm(vmin=data.performance.min(), vcenter=0, vmax=data.performance.max())
colors = [plt.cm.RdYlGn(norm(c)) for c in data['performance']]
ax = sns.barplot(x='performance', y='System', data=data, palette=colors, edgecolor='black')
plt.tight_layout()
plt.show()
Seaborn's diverging_palette can be used to create a color palette given two hue values. A hue of 0 is red, a hue of 150 is green. Default the center is white. You can experiment with saturation s=80 and lightness l=55.
red_green_pal = sns.diverging_palette(0, 150, n=256, as_cmap=True)
colors = [red_green_pal(norm(c)) for c in data['performance']]
Is there a way to rotate X-axis's tick-strings in pyqtgraph?
In matplotlib, it can be done using setp(..., rotation=xxx) (and other ways). E.g.:
import matplotlib.pyplot as plt
x = np.arange(1000)
plt.plot(x, x**2)
plt.setp(plt.xticks()[1], rotation=-45)
plt.show()