Using missing_kwds with geopandas changes the shape of the displayed map - python

I'm using Geopandas (0.11.1) to plot data on maps. I'm facing an issue with missing_kwds. As some of my values are undefined, I want them to be colored in a specific way. I do that using the missing_kwds option of the plot method.
However, when using it, the shape of the map slightly changes, which is disgraceful when switching quickly from one to the other.
Here is an example.
A map without using missing_kwds :
import geopandas
import matplotlib.pyplot as plt
df = geopandas.read_file(geopandas.datasets.get_path("naturalearth_lowres"))
df.plot()
plt.savefig('world1.png')
A map using missing_kwds :
import geopandas
import matplotlib.pyplot as plt
import numpy as np
df = geopandas.read_file(geopandas.datasets.get_path("naturalearth_lowres"))
df.loc[df.name=="China", 'pop_est'] = np.nan
df.plot(column="pop_est", missing_kwds=dict(color="lightgray"))
plt.savefig('world2.png')
Those are the two resulting maps.
world1.png:
world2.png:
In case the difference isn't clear, here is a GIF that illustrates the shape changes.
Does anyone have an idea how I could solve this issue?

Add plt.gca().set_aspect('equal') after df.plot().

Related

What is the proper way to set color for Seaborn histplot with data in ndarray format?

I am trying to plot a histogram with seaborn of multiple data in ndarray format. I am confused as to how to give different colors per column. The color parameter seems to have no effect.
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
d = np.random.random((1000,3))
sns.histplot(d, color='red')
plt.show()
When i change it to
sns.histplot(d, color='blue')
The colors do not change.
The only way to control the color is by using palette, for example:
sns.histplot(d, palette='Blues')
previously i had an error without specifying a color, but it seems to be a problem with a specific machine configuration.

Contextily add_basemap inferred zoom level is not valid and changing zoom parameter doesn't fix issue

I want to plot the background map of Melbourne behind the plotted points of property addresses.
I used the following code:
import pandas as pd
import geopandas as gpd
from shapely.geometry import shape
import matplotlib.pyplot as plt
import contextily
MELB_PROPERTY_DATA = "https://data.melbourne.vic.gov.au/resource/imwx-szwr.json"
properties = pd.read_json(MELB_PROPERTY_DATA)
properties['the_geom'] = properties['the_geom'].apply(shape)
properties_geo = gpd.GeoDataFrame(properties).set_geometry('the_geom')
ax = properties_geo.plot(markersize=1)
contextily.add_basemap(ax)
plt.show()
At the contextily.add_basemap(ax) line I get the following UserWarning.
contextily\tile.py:632: UserWarning: The inferred zoom level of 30 is
not valid for the current tile provider (valid zooms: 0 - 18).
I read the Contextily docs but they don't fix my problem.
Changing the line to contextily.add_basemap(ax, zoom=5) removes the UserWarning but still no background map appears.
Similar questions have been asked on SO, but I can't retrofit them to my problem.
I feel like I'm importing lots of libraries for this simple task as well, so if you have any suggestions to fine-tune it that would also be appreciated.
I solved this by realising from swatchai's comment that a Coordinate Reference System (CRS) was never defined.
See below for final code, with erroneous lines commented out to show the difference.
import pandas as pd
import geopandas as gpd
from shapely.geometry import shape
import matplotlib.pyplot as plt
import contextily
MELB_PROPERTY_DATA = "https://data.melbourne.vic.gov.au/resource/imwx-szwr.json"
properties = pd.read_json(MELB_PROPERTY_DATA)
properties['the_geom'] = properties['the_geom'].apply(shape)
# properties_geo = gpd.GeoDataFrame(properties).set_geometry('the_geom')
properties_geo = gpd.GeoDataFrame(properties, geometry='the_geom', crs='EPSG:4326')
ax = properties_geo.plot(markersize=1)
# contextily.add_basemap(ax)
contextily.add_basemap(ax, crs=properties_geo.crs.to_string())
plt.show()

How do I display drop shadows for line charts with Matplotlib?

I am trying to make a drop shadow for some data I have. View the current image here
I can't increase the drop shadow's width. Is there a way to do it?
Here's what I currently have:
plt.plot(times, past_values,color='red',path_effects=[path_effects.SimpleLineShadow(shadow_color="red"),path_effects.Normal()])
You need to pass the linewidth argument to SimpleLineShadow.
plt.plot(times, past_values,color='red',path_effects=[path_effects.SimpleLineShadow(shadow_color="red", linewidth=5),path_effects.Normal()])
You can simple add shadow=True
Here is an example
plt.plot(times, past_values,color='red',shadow=True)
Try it it may work
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.patheffects import PathPatchEffect, SimpleLineShadow, Normal
sns.set()
plt.plot([1,2,3],[1,4,9],color='red',path_effects=[SimpleLineShadow(shadow_color="red", linewidth=10),Normal()])

box plot not appearing in Google Colab

Trying to create a simple Box Plot using Google Colab for my Intro Python class. It is not appearing as I would like it. You can see my code and output below. I read in a file on NBA statistics, and my box plot would be based on a variable called "SHOT_CLOCK".
So far what I have:
import pandas as pd
from matplotlib import pyplot as plt
df = pd.read_csv('file path')
plt.boxplot(df['SHOT_CLOCK'], vert=False)
plt.title('Box Plot for SHOT_CLOCK')
plt.xlabel('Shot Clock')
plt.show()
Output:
Edit
In your example you are passing a Series object, try this way
plt.figure()
plt.title('Box Plot for SHOT_CLOCK')
plt.xlabel('Shot Clock')
df.boxplot(column='SHOT_CLOCK')
Once you add the following Import to your code it will work:
import matplotlib.pyplot as plt
plt.style.use('classic')
%matplotlib inline

Animated heatmap from dictionary of Pandas DataFrames

I would like to plot an animated heatmap from a group of DataFrames (for example saved in a dictionary), either as gif or a movie.
For example, say I have the following collection of DFs. I can display all of them one after the other. But I would like to have them all being shown in the same figure in the same way as a GIF is shown (a loop of the heatmaps).
import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
dataframe_collection = {}
for i in range(5):
dataframe_collection[i] = pd.DataFrame(np.random.random((5,5)))
# Here within the same loop just for brevity
sns.heatmap(dataframe_collection[i])
plt.show()
The simplest way is to first create separate png images, and then use a software such as ImageMagick to convert them to an animated gif.
Example to create the png's:
import pandas as pd
import numpy as np
from matplotlib import pyplot as plt
dataframe_collection = {}
for i in range(5):
dataframe_collection[i] = pd.DataFrame(np.random.random((5,5)))
#plt.pcolor(dataframe_collection[i])
sns.heatmap(dataframe_collection[i])
plt.gca().set_ylim(0, len(dataframe_collection[i])) #avoiding problem with axes
plt.axis('off')
plt.tight_layout()
plt.savefig(f'dataframe_{i}.png')
After installing ImageMagick the following shell command creates a gif. If the defaults are not satisfying, use the docs to explore the many options.
convert.exe -delay 20 -loop 0 dataframe_*.png dataframes.gif
See also this post about creating animations and an animated gif inside matplotlib.
Note that Seaborn's heatmap also has some features such as sns.heatmap(dataframe_collection[i], annot=True).
If you're unable to use ImageMagick, you could show a video by quickly displaying single png files, simulating a video.
This and this post contain more explanations and example code. Especially the second part of this answer looks promising.

Categories