Fixing incorrect contour lines occurring around 0 longitude - python-3.x

I'm fairly new to plotting contour lines. When plotting ice data that crosses over longitude zero in the Arctic, the contour lines create horizontal lines that span the x axis. Ideally I'd merge the lines so they created one solid contour, but failing that just removing the horizontal lines would be enough.
https://imgur.com/VU1IlNA (I'm new and not allowed to post pictures yet, but this shows the problem clearly)
from netCDF4 import Dataset, MFDataset, num2date
import numpy as np
import cartopy.crs as ccrs
from cartopy.util import add_cyclic_point
import pandas as pd
from netCDF4 import Dataset as NetCDFFile
import matplotlib.pyplot as plt
nc = NetCDFFile('LongitudeLatitudeGrid-n3125-Svalbard- from20190129.hdf')
lats = nc.variables['Latitudes'][:]
lons = nc.variables['Longitudes'][:]
nc17 = NetCDFFile('asi-AMSR2-n3125-20190517-v5.4.hdf')
ice17 = nc17.variables['ASI Ice Concentration'][:]
fig = plt.figure(figsize=(30,20))
ax6 = plt.subplot(2,3,6,projection=ccrs.Mercator(min_latitude=77,max_latitude=81))
mm = ax6.contour(lons,lats,ice17,vmin=0,vmax=100,
transform=ccrs.PlateCarree(),cmap='BuPu',zorder=1)
plt.title('May 17th stations: δ15N vaules',size='x-large')
ax6.set_extent([-10,10,77,81])
ax6.coastlines()
Expected results are a clean contour line, with no gap, but instead a gap appears as shown.

I managed to fix this, the issue was that my longitudinal values jumped from 0 to 360 at longitude zero. By subtracting 360 from all longitude values > 180 the problem was solved, and the plot looks appropriate now.

Related

problem on filing up the colour between two index values

I have a timeseries data timeseries.txt. First I select a index value (here 50) and put a red line mark on that selected index value. And I want to highlight portion before(idx-20) and after(idx+20) the red line index value on the timeseries.
I wrote this code however i am able to put the red line mark on the timeseries but while using fill_betweenx it doesnot work. I hope experts may help me overcoming this problem.Thanks.
import matplotlib.pyplot as plt
import numpy as np
input_data=np.loadtxt("timeseries.txt")
time=np.arange(len(input_data))
plt.plot(time,input_data)
idx = [50]
mark = [time[i] for i in idx]
plt.plot(idx,[input_data[i] for i in mark], marker="|",color='red',markerfacecolor='none',mew=0.4,ms=30,alpha=2.0)
plt.fill_betweenx(idx-20,idx+20 alpha=0.25,color='lightsteelblue')
plt.show()
If you are looking for just a semi-transparent rectangle, you can use patches.Rectangle to draw one. Refer here. I have updated your code to add a rectangle. See if this meets your requirement. I have used a sine wave as I didn't have your data.
import matplotlib.pyplot as plt
import numpy as np
## Create sine wave
x = np.arange(100)
input_data=np.sin(2*np.pi*3*x/100)
time=np.arange(len(input_data))
plt.plot(time,input_data)
idx = [50]
mark = [time[i] for i in idx]
plt.plot(idx,[input_data[i] for i in mark], marker="|", color='red', markerfacecolor='none', mew=0.4,ms=30,alpha=2.0)
#plt.fill_betweenx(mark,idx-20,0, alpha=0.25,color='lightsteelblue')
# Create a Rectangle patch
import matplotlib.patches as patches
from matplotlib.patches import Rectangle
plt.gca().add_patch(Rectangle((idx[0]-20, -0.15), 40, .3, facecolor = 'lightsteelblue',fill=True,alpha=0.25, lw=0))
plt.show()
EDIT
Please refer to the Rectangle documentation provided earlier in the response. You will need to adjust the start coordinates (x,y) and the height and width to see how big/small you need the Rectangle. For eg: changing the rectangle code like this...
plt.gca().add_patch(Rectangle((idx[0]-10, -0.40), 20, 0.8, facecolor = 'lightsteelblue',fill=True,alpha=0.25, lw=0))
will give you this plot.

How to set up scaled axes with hvplot?

Question:
How to set up scaled axes with hvplot? [https://hvplot.pyviz.org/]
Code example:
I have the following code giving me the figure hereafter but the lat and long axes are not equal. How to have a 1:1 ratio between the two axes?
import os, sys
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import hvplot.pandas
pos = pd.read_csv(os.path.join('my_gps_positions.csv'))
pos.hvplot.scatter(*'lat lng'.split())
Here's the GPS file content:
And the resulting graph with unequal axes in my notebook:
You could adjust the width and the height parameter of the plot:
df.hvplot.scatter(x='lat', y='lon', width=500, height=500)
Or do you mean the range of the axes? They can be set by parameter xlim and ylim, for example:
df.hvplot.scatter(x='lat', y='lon', xlim=(6, 8), ylim=(45, 47))
Since you're plotting latitudes and longitudes you should definitely take a look at geoviews: http://geoviews.org/
longitude and latitude units should not be the same distance, except right on the equator. So normally you would plot these with hvplot as:
df.hvplot.points(x='lon', y='lat', geo=True, tiles='OSM')
but if you really want to force them to be the same, you can use aspect:
df.hvplot.scatter(x='lon', y='lat', aspect='equal')

Issue with drawparallels argument in Basemap

This seems like it should be an easy fix but I can't get it to work. I would like 40°N to display in the attached plot, but setting the labels argument in drawparallels to [1,0,1,1] isn't doing the trick. That should plot the parallels lables where they intersect the left, top and bottom of the plot according to the documentation. I would also like for 0° to once again show up in the bottom right corner. Any idea of how I can fix those 2 issues?
from netCDF4 import Dataset as NetCDFFile
import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.basemap import Basemap
from mpl_toolkits.basemap import addcyclic
nc = NetCDFFile('C:/myfile.nc')
lat = nc.variables['lat'][:]
lon = nc.variables['lon'][:]
time = nc.variables['time'][:]
olr = nc.variables['olr'][:]
olr,lon = addcyclic(olr,lon)
map = Basemap(llcrnrlon=0.,llcrnrlat=-40.,urcrnrlon=360.,urcrnrlat=40.,resolution='l')
lons,lats = np.meshgrid(lon,lat)
x,y = map(lons,lats)
levels = np.arange(-19.5,20.0,0.5)
levels = levels[levels!=0]
ticks = np.arange(-20.0,20.0,4.0)
cs = map.contourf(x,y,olr[0],levels, cmap='bwr')
cbar = plt.colorbar(cs, orientation='horizontal', cmap='bwr', spacing='proportional', ticks=ticks)
cbar.set_label('Outgoing Longwave Radiation Anomalies $\mathregular{(W/m^2)}$')
map.drawcoastlines()
map.drawparallels(np.arange(-40,40,20),labels=[1,0,1,1], linewidth=0.5, fontsize=7)
map.drawmeridians(np.arange(0,360,40),labels=[1,1,0,1], linewidth=0.5, fontsize=7)
The first part of the question is easy. In order for the label to show up, you have to actually draw the parallel, but np.arange(-40,40,20) does not include 40. So, if you change that statement to np.arange(-40,41,20) your 40N label will show up.
The second part should in principle be solvable in the same way, but Basemap apparently uses the modulo of the longitudes to compute the position of the labels, so just using np.arange(0,361,40) when drawing the meridians will result in two 0 labels on top of each other. However, we can capture the labels that drawmeridians generates and manually change the position of the second 0 label. The labels are stored in a dictionary, so they are easy to deal with. To compute the x position of the last label, I compute the difference in x-position between the first and the second label, multiply that with the amount of meridians to be drawn (360/40) and add the x-position of the first label.
Here the complete example:
import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.basemap import Basemap
map = Basemap(llcrnrlon=0.,llcrnrlat=-40.,urcrnrlon=360.,urcrnrlat=40.,resolution='l')
map.drawcoastlines()
yticks = map.drawparallels(
np.arange(-40,41,20),labels=[1,0,1,1], linewidth=0.5, fontsize=7
)
xticks = map.drawmeridians(
np.arange(0,361,40),labels=[1,1,0,1], linewidth=0.5, fontsize=7
)
first_pos = xticks[0][1][0].get_position()
second_pos = xticks[40][1][0].get_position()
last_x = first_pos[0]+(second_pos[0]-first_pos[0])*360/40
xticks[360][1][0].set_position((last_x,first_pos[1]))
plt.show()
Here the resulting plot:
Hope this helps.

Having trouble with multiple figures on pyplot

I am currently going through the Kaggle Titanic Machine Learning thing and using http://nbviewer.jupyter.org/github/donnemartin/data-science-ipython-notebooks/blob/master/kaggle/titanic.ipynb to figure it out as I am a relative beginner to Python. I thought I understood what the first few steps were doing and I am trying to recreate an earlier step by making a figure with multiple plots on it. I can't seem to get the plots to actually show up.
Here is my code:
`
import pandas as pd
import numpy as np
import pylab as plt
train=pd.read_csv("train.csv")
#Set the global default size of matplotlib figures
plt.rc('figure', figsize=(10, 5))
#Size of matplotlib figures that contain subplots
figsize_with_subplots = (10, 10)
# Size of matplotlib histogram bins
bin_size = 10
females_df = train[train['Sex']== 'female']
print("females_df", females_df)
females_xt = pd.crosstab(females_df['Pclass'],train['Survived'])
females_xt_pct = females_xt.div(females_xt.sum(1).astype(float), axis = 0)
males = train[train['Sex'] == 'male']
males_xt = pd.crosstab(males['Pclass'], train['Survived'])
males_xt_pct= males_xt.div(males_xt.sum(1).astype(float), axis = 0)
plt.figure(5)
plt.subplot(221)
females_xt_pct.plot(kind='bar', title='Female Survival Rate by Pclass')
plt.xlabel('Passenger Class')
plt.ylabel('Survival Rate')
plt.subplot(222)
males_xt_pct.plot(kind='bar', title= 'Male Survival Rate by Pclass')
plt.xlabel('Passenger Class')
plt.ylabel('Survival Rate')
`
And this is displaying two blank plots separately (one in the 221 location, and then next plot on a new figure in the 222 location) and then another plot with males that actually works at the end. What am I doing wrong here?
In order to plot the pandas plot to apreviously created subplot, you may use the ax argument of the pandas plotting function.
ax=plt.subplot(..)
df.plot(..., ax=ax)
So in this case the code may look like
plt.figure(5)
ax=plt.subplot(221)
females_xt_pct.plot(kind='bar', title='Female Survival Rate by Pclass',ax=ax)
ax2=plt.subplot(222)
males_xt_pct.plot(kind='bar', title= 'Male Survival Rate by Pclass',ax=ax2)

Matplotlib: personalize imshow axis

I have the results of a (H,ranges) = numpy.histogram2d() computation and I'm trying to plot it.
Given H I can easily put it into plt.imshow(H) to get the corresponding image. (see http://matplotlib.org/api/pyplot_api.html#matplotlib.pyplot.imshow )
My problem is that the axis of the produced image are the "cell counting" of H and are completely unrelated to the values of ranges.
I know I can use the keyword extent (as pointed in: Change values on matplotlib imshow() graph axis ). But this solution does not work for me: my values on range are not growing linearly (actually they are going exponentially)
My question is: How can I put the value of range in plt.imshow()? Or at least, or can I manually set the label values of the plt.imshow resulting object?
Editing the extent is not a good solution.
You can just change the tick labels to something more appropriate for your data.
For example, here we'll set every 5th pixel to an exponential function:
import numpy as np
import matplotlib.pyplot as plt
im = np.random.rand(21,21)
fig,(ax1,ax2) = plt.subplots(1,2)
ax1.imshow(im)
ax2.imshow(im)
# Where we want the ticks, in pixel locations
ticks = np.linspace(0,20,5)
# What those pixel locations correspond to in data coordinates.
# Also set the float format here
ticklabels = ["{:6.2f}".format(i) for i in np.exp(ticks/5)]
ax2.set_xticks(ticks)
ax2.set_xticklabels(ticklabels)
ax2.set_yticks(ticks)
ax2.set_yticklabels(ticklabels)
plt.show()
Expanding a bit on #thomas answer
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.image as mi
im = np.random.rand(20, 20)
ticks = np.exp(np.linspace(0, 10, 20))
fig, ax = plt.subplots()
ax.pcolor(ticks, ticks, im, cmap='viridis')
ax.set_yscale('log')
ax.set_xscale('log')
ax.set_xlim([1, np.exp(10)])
ax.set_ylim([1, np.exp(10)])
By letting mpl take care of the non-linear mapping you can now accurately over-plot other artists. There is a performance hit for this (as pcolor is more expensive to draw than AxesImage), but getting accurate ticks is worth it.
imshow is for displaying images, so it does not support x and y bins.
You could either use pcolor instead,
H,xedges,yedges = np.histogram2d()
plt.pcolor(xedges,yedges,H)
or use plt.hist2d which directly plots your histogram.

Resources