3D interpolation in Python Pandas using a mesh grid - python

I have the following 10 lines of a large Pandas dataframe df;
X,Y,Z are grid points in xyz-direction; U,V,W are (measured) velocity components in x,y,z-direction.
X Y Z U V W
0 -201.0 -2.00 11.200 3.750 -15.20 -0.75800
1 -201.0 -2.00 12.220 3.640 -15.40 -0.71100
2 -200.0 -3.00 1.079 -1.480 -3.86 0.03670
3 -198.0 -3.00 7.190 4.220 -13.50 -1.31000
4 -198.0 -1.43 5.530 3.510 -10.10 -1.56000
5 -195.0 -1.43 6.140 3.900 -11.80 -1.50000
6 -195.0 -2.54 0.000 -0.767 -5.19 0.00154
7 -195.0 -3.54 0.600 -1.210 -6.04 -0.05580
8 -191.0 -5.54 1.449 -1.510 -2.80 -0.20900
9 -191.0 -7.54 2.392 -0.782 -2.65 -0.56000
I want to now interpolate the values U,V,W over a finer 5x5x5 grid in X,Y,Z.
x = np.arange(-200, -175, 5)
y = np.arange(-10, 5, 5)
z = np.arange(0,20,5)
xx, yy, zz = np.meshgrid(x, y,z )
NT = np.product(xx.shape)
data_grid = {
"x_grid": np.reshape(xx,NT),
"y_grid": np.reshape(yy,NT),
"z_grid": np.reshape(zz,NT)
}
df2 = pd.DataFrame(data= data_grid)
I see scipy has this interpolate griddata function which I am trying to call (for now I only interpolate U in XYZ).
xp = df['X'].to_numpy()
yp = df['Y'].to_numpy()
zp = df['Z'].to_numpy()
up = df['U'].to_numpy()
U_grid = griddata([(xp,yp,zp)], up, [(x_grid,y_grid,z_grid)], method='nearest')
But this gives me:
"ValueError: different number of values and points"
What do I do wrong?

Related

Geopandas: dataframe to geodataframe with different espg code

I have a dataframe (df2): wherein x,y are specified in rd new epsg:28992 coordinates.
x y z batch_nr batch_description
0 117298.377 560406.392 0.612 5800 PRF Grasland (l)
1 117297.803 560411.756 1.015
2 117296.327 560419.840 1.580
3 117295.470 560425.716 2.490
4 117296.875 560429.976 4.529
more CRS info:
# def CRS, used in geopandas
from pyproj import CRS
crs_rd = CRS.from_user_input(28992)
crs_rd
<Derived Projected CRS: EPSG:28992>
Name: Amersfoort / RD New
Axis Info [cartesian]:
- X[east]: Easting (metre)
- Y[north]: Northing (metre)
Area of Use:
- name: Netherlands - onshore, including Waddenzee, Dutch Wadden Islands and 12-mile offshore coastal zone.
- bounds: (3.2, 50.75, 7.22, 53.7)
Coordinate Operation:
- name: RD New
- method: Oblique Stereographic
Datum: Amersfoort
- Ellipsoid: Bessel 1841
- Prime Meridian: Greenwich
How can I convert df2 to a geodatafame where the geometry is set as CRS: EPSG 28992?
It's a simple case of using GeoPandas constructor with crs parameter and points_from_xy()
import geopandas as gpd
import pandas as pd
import io
df2 = pd.read_csv(io.StringIO(""" x y z batch_nr batch_description
0 117298.377 560406.392 0.612 5800 PRF Grasland (l)
1 117297.803 560411.756 1.015
2 117296.327 560419.840 1.580
3 117295.470 560425.716 2.490
4 117296.875 560429.976 4.529"""), sep="\s\s+", engine="python")
gdf = gpd.GeoDataFrame(df2, geometry=gpd.points_from_xy(df2["x"], df2["y"], df2["z"]), crs="epsg:28992")
gdf
output
x
y
z
batch_nr
batch_description
geometry
0
117298
560406
0.612
5800
PRF Grasland (l)
POINT Z (117298.377 560406.392 0.612)
1
117298
560412
1.015
nan
POINT Z (117297.803 560411.756 1.015)
2
117296
560420
1.58
nan
POINT Z (117296.327 560419.84 1.58)
3
117295
560426
2.49
nan
POINT Z (117295.47 560425.716 2.49)
4
117297
560430
4.529
nan
POINT Z (117296.875 560429.976 4.529)

How to draw cumulative density plot from pandas?

I have a dataframe:
count_single count_multi column_names
0 11345 7209 e
1 11125 6607 w
2 10421 5105 j
3 9840 4478 r
4 9561 5492 f
5 8317 3937 i
6 7808 3795 l
7 7240 4219 u
8 6915 3854 s
9 6639 2750 n
10 6340 2465 b
11 5627 2834 y
12 4783 2384 c
13 4401 1698 p
14 3305 1753 g
15 3283 1300 o
16 2767 1697 t
17 2453 1276 h
18 2125 1140 a
19 2090 929 q
20 1330 518 d
I want to visualize the single count and multi_count while column_names as a common column in both of them. I am looking something like this :
What I've tried:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
sns.set_context('paper')
f, ax = plt.subplots(figsize = (6,15))
sns.set_color_codes('pastel')
sns.barplot(x = 'count_single', y = 'column_names', data = df,
label = 'Type_1', color = 'orange', edgecolor = 'w')
sns.set_color_codes('muted')
sns.barplot(x = 'count_multi', y = 'column_names', data = df,
label = 'Type_2', color = 'green', edgecolor = 'w')
ax.legend(ncol = 2, loc = 'lower right')
sns.despine(left = True, bottom = True)
plt.show()
it's giving me plot like this:
How to visualize these two columns with same as expected images?
I really appreciate any help you can provide.
# instantiate figure with two rows and one column
fig, axes = plt.subplots(nrows=2, figsize=(10,5))
# plot barplot in the first row
df.set_index('column_names').plot.bar(ax=axes[0], color=['rosybrown', 'tomato'])
# first scale each column bydividing by its sum and then use cumulative sum to generate the cumulative density function. plot on the second ax
df.set_index('column_names').apply(lambda x: x/x.sum()).cumsum().plot(ax=axes[1], color=['rosybrown', 'tomato'])
# change ticks in first plot:
axes[0].set_yticks(np.linspace(0, 12000, 7)) # this means: make 7 ticks between 0 and 12000
# adjust the axislabels for the second plot
axes[1].set_xticks(range(len(df)))
axes[1].set_xticklabels(df['column_names'], rotation=90)
plt.tight_layout()

How to plot a separator line between two data classes?

I have a simple exercise that I am not sure how to do. I have the following data sets:
male100
Year Time
0 1896 12.00
1 1900 11.00
2 1904 11.00
3 1906 11.20
4 1908 10.80
5 1912 10.80
6 1920 10.80
7 1924 10.60
8 1928 10.80
9 1932 10.30
10 1936 10.30
11 1948 10.30
12 1952 10.40
13 1956 10.50
14 1960 10.20
15 1964 10.00
16 1968 9.95
17 1972 10.14
18 1976 10.06
19 1980 10.25
20 1984 9.99
21 1988 9.92
22 1992 9.96
23 1996 9.84
24 2000 9.87
25 2004 9.85
26 2008 9.69
and the second one:
female100
Year Time
0 1928 12.20
1 1932 11.90
2 1936 11.50
3 1948 11.90
4 1952 11.50
5 1956 11.50
6 1960 11.00
7 1964 11.40
8 1968 11.00
9 1972 11.07
10 1976 11.08
11 1980 11.06
12 1984 10.97
13 1988 10.54
14 1992 10.82
15 1996 10.94
16 2000 11.12
17 2004 10.93
18 2008 10.78
I have the following code:
y = -0.014*male100['Year']+38
plt.plot(male100['Year'],y,'r-',color = 'b')
ax = plt.gca() # gca stands for 'get current axis'
ax = male100.plot(x=0,y=1, kind ='scatter', color='g', label="Mens 100m", ax = ax)
female100.plot(x=0,y=1, kind ='scatter', color='r', label="Womens 100m", ax = ax)
Which produces this result:
I need to plot a line that would go exactly between them. So the line would leave all of the green points below it, and the red point above it. How do I do so?
I've tried playing with the parameters of y, but to no avail. I also tried fitting a linear regression to male100 , female100 , and the merged version of them (across rows), but couldn't get any results.
Any help would be appreciated!
A solution is using support vector machine (SVM). You can find two margins that separate two classes of points. Then, the average line of two support vectors is your answer. Notice that it's happened just when these two set of points are linearly separable.
You can use the following code to see the result:
Data Entry
male = [
(1896 , 12.00),
(1900 , 11.00),
(1904 , 11.00),
(1906 , 11.20),
(1908 , 10.80),
(1912 , 10.80),
(1920 , 10.80),
(1924 , 10.60),
(1928 , 10.80),
(1932 , 10.30),
(1936 , 10.30),
(1948 , 10.30),
(1952 , 10.40),
(1956 , 10.50),
(1960 , 10.20),
(1964 , 10.00),
(1968 , 9.95),
(1972 , 10.14),
(1976 , 10.06),
(1980 , 10.25),
(1984 , 9.99),
(1988 , 9.92),
(1992 , 9.96),
(1996 , 9.84),
(2000 , 9.87),
(2004 , 9.85),
(2008 , 9.69)
]
female = [
(1928, 12.20),
(1932, 11.90),
(1936, 11.50),
(1948, 11.90),
(1952, 11.50),
(1956, 11.50),
(1960, 11.00),
(1964, 11.40),
(1968, 11.00),
(1972, 11.07),
(1976, 11.08),
(1980, 11.06),
(1984, 10.97),
(1988, 10.54),
(1992, 10.82),
(1996, 10.94),
(2000, 11.12),
(2004, 10.93),
(2008, 10.78)
]
Main Code
Notice that the value of C is important here. If it is selected to 1, you can't get the preferred result.
from sklearn import svm
import numpy as np
import matplotlib.pyplot as plt
X = np.array(male + female)
Y = np.array([0] * len(male) + [1] * len(female))
# fit the model
clf = svm.SVC(kernel='linear', C=1000) # C is important here
clf.fit(X, Y)
plt.figure(figsize=(8, 4))
# get the separating hyperplane
w = clf.coef_[0]
a = -w[0] / w[1]
xx = np.linspace(-1000, 10000)
yy = a * xx - (clf.intercept_[0]) / w[1]
plt.figure(1, figsize=(4, 3))
plt.clf()
plt.plot(xx, yy, "k-") #********* This is the separator line ************
plt.scatter(X[:, 0], X[:, 1], c=Y, zorder=10, cmap=plt.cm.Paired,
edgecolors="k")
plt.xlim((1890, 2010))
plt.ylim((9, 13))
plt.show()
I believe your idea of making use of regression lines is correct - if they aren't used, the line would be merely superficial (and impossible to justify if the points overlap in the event of messy data).
Therefore, using some randomly made data with a known linear relationship, we can do the following:
import random
import numpy as np
from matplotlib import pyplot as plt
from sklearn.linear_model import LinearRegression
x_values = np.arange(0, 51, 1)
y_points_1 = [i * 2 + random.randint(5, 30) for i in x_points]
y_points_2 = [i - random.randint(5, 30) for i in x_points]
x_points = x_values.reshape(-1, 1)
def regression(x, y):
model = LinearRegression().fit(x, y)
y_pred = model.predict(x)
return y_pred
barrier = [(regression(x=x_points, y=y_points_1)[i] + value) / 2 for i, value in enumerate(regression(x=x_points, y=y_points_2))]
plt.plot(x_points, regression(x=x_points, y=y_points_1))
plt.plot(x_points, regression(x=x_points, y=y_points_2))
plt.plot(x_points, barrier)
plt.scatter(x_values, y_points_1)
plt.scatter(x_values, y_points_2)
plt.grid(True)
plt.show()
Giving us the following plot:
This method also works for an overlap in the data points, so if we change the random data slightly and apply the same process:
x_values = np.arange(0, 51, 1)
y_points_1 = [i * 2 + random.randint(-10, 30) for i in x_points]
y_points_2 = [i - random.randint(-10, 30) for i in x_points]
We get something like the following:
It is important to note that the lists used here are of the same length, so you would need to add some predicted points to the female data after applying regression in order to make use of the line between them. These points would merely be along the regression line with the x-values corresponding to those present in the male data.
Because sklearn might be a bit over the top for a linear fit and to get rid of the condition that you would need the same number of data points for male and female data, here the same implementation with numpy.polyfit. This also demonstrates that their approach is not a solution to the problem.
import pandas as pd
import numpy as np
from matplotlib import pyplot as plt
#data import
male = pd.read_csv("test1.txt", delim_whitespace=True)
female = pd.read_csv("test2.txt", delim_whitespace=True)
#linear fit of both populations
pmale = np.polyfit(male.Year, male.Time, 1)
pfemale = np.polyfit(female.Year, female.Time, 1)
#more appealing presentation, let's pretend we do not just fit a line
x_fitmin=min(male.Year.min(), female.Year.min())
x_fitmax=max(male.Year.max(), female.Year.max())
x_fit=np.linspace(x_fitmin, x_fitmax, 100)
#create functions for the three fit lines
male_fit = np.poly1d(pmale)
print(male_fit)
female_fit = np.poly1d(pfemale)
print(female_fit)
sep = np.poly1d(np.mean([pmale, pfemale], axis=0))
print(sep)
#plot all markers and lines
ax = male.plot(x="Year", y="Time", c="blue", kind="scatter", label="male")
female.plot(x="Year", y="Time", c="red", kind="scatter", ax=ax, label="female")
ax.plot(x_fit, male_fit(x_fit), c="blue", ls="dotted", label="male fit")
ax.plot(x_fit, female_fit(x_fit), c="red", ls="dotted", label="female fit")
ax.plot(x_fit, sep(x_fit), c="black", ls="dashed", label="separator")
plt.legend()
plt.show()
Sample output:
-0.01333 x + 36.42
-0.01507 x + 40.92
-0.0142 x + 38.67
And one point is still in the wrong section. However - I find this question so interesting because I expected answers from the sklearn crowd for non-linear data groups. I even installed sklearn in anticipation! If in the next days nobody posts a good solution
with SVMs, I will set a bounty on this question.
One solution is the geometrical approach. You can find the convex hull of each data class, then find a line that goes through these two convex hulls. To find the line, you can find inner tangent line between two convex hulls using this code, and rotate it a little bit.
You can use the following code:
from scipy.spatial import ConvexHull, convex_hull_plot_2d
male = np.array(male)
female = np.array(female)
hull_male = ConvexHull(male)
hull_female = ConvexHull(female)
plt.plot(male[:,0], male[:,1], 'o')
for simplex in hull_male.simplices:
plt.plot(male[simplex, 0], male[simplex, 1], 'k-')
# Here, the separator line comes from SMV‌ result.
# Just to show the a separator as an exmple
# plt.plot(xx, yy, "k-")
plt.plot(female[:,0], female[:,1], 'o')
for simplex in hull_female.simplices:
plt.plot(female[simplex, 0], female[simplex, 1], 'k-')
plt.xlim((1890, 2010))
plt.ylim((9, 13))

overlaying a basemap on contours

This question is a follow-up to an earlier question and from #JoeKington here. Both of these solutions work excellently for my needs.
However I have been trying to overlay a basemap on the contours. Going by the example here http://matplotlib.org/basemap/users/examples.html, I do not seem to get it right. I think my basic problem is to convert the contour x,y values into map coordinates. I reproduce below the codes for 1) contours (as given by #usethedeathstar, which works very well) and 2) the map object and the plotting.
#!/usr/bin/python
from mpl_toolkits.basemap import Basemap
import numpy as np
from scipy.interpolate import griddata
class d():
def __init__(self):
A0 = open("meansr2.txt","rb") #
A1 = A0.readlines()
A = np.zeros((len(A1),3))
for i, l in enumerate(A1):
li = l.split()
A[i,0] = float(li[0])
A[i,1] = float(li[1])
A[i,2] = float(li[2])
self.Lon = A[:,0]
self.Lat = A[:,1]
self.Z = A[:,2]
data = d()
numcols, numrows = 300, 300
xi = np.linspace(data.Lon.min(), data.Lon.max(), numrows)
yi = np.linspace(data.Lat.min(), data.Lat.max(), numcols)
xi, yi = np.meshgrid(xi, yi)
x, y, z = data.Lon, data.Lat, data.Z
points = np.vstack((x,y)).T
values = z
wanted = (xi, yi)
zi = griddata(points, values, wanted)
Defining map object
m = Basemap(projection = 'merc',llcrnrlon = 21, llcrnrlat = -18, urcrnrlon = 34, urcrnrlat = -8, resolution='h')
m.drawcountries(linewidth=0.5, linestyle='solid', color='k', antialiased=1, ax=None, zorder=None)
m.drawmapboundary(fill_color = 'white')
m.fillcontinents(color='coral',lake_color='blue')
parallels = np.arange(-18, -8, 2.)
m.drawparallels(parallels, color = 'black', linewidth = 0.5, labels=[True,False,False,False])
meridians = np.arange(22,34, 2.)
m.drawmeridians(meridians, color = '0.25', linewidth = 0.5, labels=[False,False,False,True])
import pylab as plt
an attempt to transform form lat/lon to map coordinates
#lat = list(data.Lat)
#lon = list(data.Lon)
#x, y = m(lon,lat)
comment:
contourf is tried with (x, y, zi), then all the above definitions are rewritten with xi, # yi, including many different attempts to redefine x,y and lon, lat.J
The plot functions
fig = plt.figure(0, figsize=(8,4.5))
im = plt.contourf(xi, yi, zi)
plt.scatter(data.Lon, data.Lat, c= data.Z)
plt.colorbar()
plt.show()
The above give two plots side by side.
Here is some data in case there is need to test
Lon Lat Z Z2 pos
32.6 -13.6 41 9 CHIP
27.1 -16.9 43 12 CHOM
32.7 -10.2 46 14 ISOK
24.2 -13.6 33 13 KABO
28.5 -14.4 43 11 KABW
28.1 -12.6 33 16 KAFI
27.9 -15.8 46 13 KAFU
24.8 -14.8 44 9 KAOM
31.1 -10.2 35 14 KASA
25.9 -13.5 24 8 KASE
29.1 -9.8 10 13 KAWA
25.8 -17.8 39 11 LIVI
33.2 -12.3 44 8 LUND
28.3 -15.4 46 12 LUSA
27.6 -16.1 47 9 MAGO
28.9 -11.1 31 15 MANS
31.3 -8.9 39 9 MBAL
31.9 -13.3 45 9 MFUW
23.1 -15.3 31 9 MONG
31.4 -11.9 39 9 MPIK
27.1 -15.0 42 12 MUMB
24.4 -11.8 15 9 MWIN
28.6 -13.0 39 9 NDOL
31.3 -14.3 44 12 PETA
23.3 -16.1 39 5 SENA
30.2 -13.2 38 11 SERE
24.3 -17.5 32 10 SESH
26.4 -12.2 23 12 SOLW
23.1 -13.5 27 14 ZAMB
Any assistance will be appreciated
I would like to thank all those who have looked at my problem and may have tried to work on it. By consistent trying, it has come to my attention that the overlaying of the basemap on the contours actually works with the following lines
After the map object definition
m = Basemap(projection = 'merc',llcrnrlon = 21, llcrnrlat = -18, urcrnrlon = 34, urcrnrlat = -8, resolution='h')
I have
x, y = m(xi, yi)
fig=plt.figure(figsize=(8,4.5))
cs = m.contour(x,y,zi,colors='b',linewidths=1.)
Contour(x,y,zi) plots the contours on the map. Since I was using contourf, I still have to find out why contourf does not give me the filled contours.
Thank you very much all for the patience and tolerance.

python Contour plot

I have a file which looks like this:
1237665126927237227 7.49126127875 1500 7.0
1237665126927237227 6.64062342139 1750 7.0
1237665126927237227 5.79903397289 2000 7.0
1237665126927237227 7.24807646775 1500 7.5
1237665126927237227 6.51250095795 1750 7.5
1237665126927237227 5.74908888515 2000 7.5
1237665126927237227 6.91915170741 1500 8.0
1237665126927237227 6.29638684709 1750 8.0
1237665126927237227 5.62891381033 2000 8.0
1237665126927237227 6.54437390102 1500 8.5
1237665126927237227 5.98359412299 1750 8.5
1237665126927237227 5.43512459898 2000 8.5
etc
I need to create a plot with the 3rd column as the x axis and 4th column as the y axis, with the 2nd column as a contour on it, with contour lines at 1,2,3,4 and so on.
Im am trying to do something along the lines of,
from pylab import *
ChiTable= np.loadtxt('ChiTableSingle.txt')
xlist = linspace(ChiTable[2])
ylist = linspace(ChiTable[3])
X, Y = meshgrid (xlist, ylist)
Z =partsChi[1]
figure()
CP1 = contour(X, Y, Z)
clabel(CP1, inline=True, fontsize=10)
pl.show()
but im just getting myself totally confused by it all. Im getting an error saying z input needs to be a 2d array, which i can understrnd why as ive made X,Y into a 2d array, and z needs to be values matching up to this, but ive got no idea how id go about that.
You need to reshape your data, not use meshgrid.
Something like:
xdim = 3
ydim = 3
X = np.asarray(ChiTable[2]).reshape((xdim, ydim))
Y = np.asarray(ChiTable[3]).reshape((xdim, ydim))
Z = np.asarray(ChiTable[1]).reshape((xdim, ydim))
contour(X, Y, Z)
meshgrid takes in two 1-D arrays and gives you back a cross of them, reshape changes an array with N total number of elements into an array with the same number of elements, but shaped differently.

Categories

Resources