Построение точечной диаграммы в python3, где ось x — широта / долгота в км, а ось y — глубина

#python #matplotlib #plot #seaborn #haversine

#python #matplotlib #график #seaborn #haversine

Вопрос:

Я пытаюсь найти наилучший способ построения некоторых данных. В принципе, у меня есть файл данных, в котором есть столбцы latitude, longitude, depth, sample_ID, Group_ID. Я хотел бы сгенерировать 2-мерную точечную диаграмму, где y — глубина, а x — расстояние в км с севера на юг (или рассчитать расстояния в разрезе относительно первой станции, отобранной в указанной ориентации), аналогично карте в стиле ODV, подобной приведенной ниже:

введите описание изображения здесь

ОБНОВЛЕНО

Я хотел добавить немного больше информации к моему первоначальному вопросу. После еще нескольких поисков и тестирования я нашел возможное решение в R, используя пакет geosphere и функцию distGeo для преобразования моих координат в расстояние в км, которое затем можно отобразить. (https://www.rdocumentation.org/packages/geosphere/versions/1.5-10/topics/distGeo )

Если кто-нибудь знает способ python сделать это, это было бы здорово!

ОБНОВЛЕНО

Однако ODV не позволяет мне выполнять необходимые настройки. Я хотел бы сгенерировать такой график, где я могу указать переменную метаданных для раскрашивания точек. Чтобы быть более конкретным по столбцу group_ID в моем файле данных, который показан в примере моего файла ниже.

 Latitude    Longitude   Depth_m Sample_ID   Group_ID
49.7225 -42.4467    10  S1  1
49.7225 -42.4467    50  S2  1
49.7225 -42.4467    75  S3  1
49.7225 -42.4467    101 S4  1
49.7225 -42.4467    152 S5  1
49.7225 -42.4467    199 S6  1
46.312  -39.658 10  S7  2
46.312  -39.658 49  S8  2
46.312  -39.658 73  S9  2
46.312  -39.658 100 S10 2
46.312  -39.658 153 S11 2
46.312  -39.658 198 S12 2
 

Это доставляло мне много проблем, пытаясь понять это. Я рассчитал расстояние между координатами, используя вычисление haversine, но как только я туда доберусь, я не уверен, как использовать эти расстояния для включения в точечную диаграмму. Это то, что у меня есть до сих пор:

 import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
#import haversine as hs
from math import radians
from sklearn.neighbors import DistanceMetric
df=pd.read_csv("locations.csv",sep="t",index_col="Sample_ID")
#plt.scatter(df['Latitude'], df['Depth_m'])
#plt.show()
df['Latitude'] = np.radians(df['Latitude'])
df['Longitude'] = np.radians(df['Longitude'])
dist = DistanceMetric.get_metric('haversine')
x = dist.pairwise(np.unique(df[['Latitude','Longitude']].to_numpy(),axis=0))*6373
print(x)
 

Этот код выдает мне матрицу расстояний для моих координат, но я, честно говоря, не могу понять, как это сделать и перенести на точечную диаграмму, которая устанавливает ось x с севера на юг. Тем более, что существует несколько глубин с одной и той же координатой, которые необходимо учитывать. Любая помощь в построении графика очень ценится!

Комментарии:

1. Что вы хотите по оси x конкретно для всех xticks? Для вывода вашего примера я получаю: [[ 0. 432.30988584] [432.30988584 0. ]]

2. Итак, теоретически я хочу, чтобы на оси X точка ‘0’ оси начиналась с самой северной координаты, которая должна быть (49.7225, -42.4467), а затем остальные координаты будут вычисляться как расстояние от этой координаты в км. Возможно, мне потребуется пересмотреть мои вычисления haversine. Текущая переменная x была матрицей расстояний, но может быть проще использовать функцию haversine, которая должна быть просто haversin(loc1, loc2)

Ответ №1:

Для вычисления расстояния вы можете использовать пакет geopy, в частности geopy.distance.geodesic(), для вычисления расстояния по дуге, предполагая конкретный эллипсоид (например, WGS84).

Чтобы сгенерировать график, аналогичный описанному вами, вы можете использовать функциональность диаграммы рассеяния библиотеки matplotlib, в частности matplotlib.pyplot.scatter() .

Приведенный ниже пример кода поможет вам выполнить как вычисление расстояния (расстояние от некоторой эталонной широты / длины до другой широты / длины… это не обязательно компонент N-S, но его достаточно легко вычислить). А также как сгенерировать точечную диаграмму, используя ваше поле Group_ID, чтобы раскрасить точки, используя два метода.

 import matplotlib.pyplot as plt
import geopy
import pandas as pd

# Load your sample data to a Pandas DataFrame where each column corresponds to
# 'Latitude', 'Longitude', 'Depth_m', 'Sample_ID', 'Group_ID'
datafile = r'<path to a file containing your data>'
df = pd.read_csv(datafile)

# Defining one end of our arc to calculate distance along (arbitrarily taking 
# the first point in the example data as the reference point).
ref_point = (df['Latitude'].iloc[0], df['Longitude'].iloc[0])

#  Loop over each sample location calculating the distance along the arc using
#  pygeo.distance.geodesic function.
dist = []
for i in range(len(df)):
    cur_point = (df['Latitude'].iloc[i], df['Longitude'].iloc[i])
    cur_geodesic = geopy.distance.geodesic(ref_point, cur_point)
    cur_dist = cur_geodesic.km
    dist.append(cur_dist)

# Add computed distances to the df DataFrame as column 'Distance_km'
df['Distance_km'] = dist

# Create a matplotlib figure and add two axes for plotting
fig = plt.figure()
ax1 = fig.add_subplot(211)
ax2 = fig.add_subplot(212)

# Example 1: of creating a scatter plot using the calculated distance field and
# colouring the points using a numeric field (i.e. Group_ID in this case is numeric)
pts = ax1.scatter(df['Distance_km'], df['Depth_m'], s=30, c=df['Group_ID'], cmap=plt.cm.jet)
plt.colorbar(pts, ax=ax1)

ax1.set_xlabel('Arc Distance from Reference Point (km)')
ax1.set_ylabel('Depth (m)')
ax1.set_title('Colouring Points by a Numeric Field')
ax1.invert_yaxis()
ax1.grid(True)

# Example of creating basically the same scatter plot as above but handling the
# case of non-numeric values in the field to be used for colour (e.g. imagine 
# wanting to the the Sample_ID field instead)
groups = list(set(df['Group_ID'])) # get a list of the unique Group_ID values
for gid in groups:
    df_tmp = df[df['Group_ID'] == gid]
    ax2.scatter(df_tmp['Distance_km'], df_tmp['Depth_m'], s=30, label=gid)
    
ax2.legend(loc='upper center', title='Legend')
ax2.set_xlabel('Arc Distance from Reference Point (km)')
ax2.set_ylabel('Depth (m)')
ax2.set_title('Colouring Points with Using Categorical Values')
ax2.invert_yaxis()
ax2.grid(True)

fig.tight_layout()
plt.show()
 

И выводимая цифра…
введите описание изображения здесь

Ответ №2:

Я не уверен, что вы пытаетесь сделать с расстоянием, но концептуально вам нужно получить вывод x в свой фрейм данных в виде нового столбца, как я это сделал.Что касается наличия другого цвета для групп, я бы использовал seaborn для этого, поскольку у них есть hue параметр. Пожалуйста, ознакомьтесь с выводом ниже вашей первой диаграммы рассеяния и попыткой того, что вы пытаетесь сделать со своей второй диаграммой рассеяния:

 import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
from math import radians
from sklearn.neighbors import DistanceMetric
import seaborn as sns
fig, ax = plt.subplots(nrows=2)
sns.scatterplot(data=df, x='Latitude', y='Depth_m', hue='Group_ID', ax=ax[0])
df['Latitude'] = np.radians(df['Latitude'])
df['Longitude'] = np.radians(df['Longitude'])
dist = DistanceMetric.get_metric('haversine')
df['Distance'] = (dist.pairwise(df[['Latitude','Longitude']].to_numpy())*6373)[0]
sns.scatterplot(data=df, x='Distance' , y='Depth_m', hue='Group_ID', ax=ax[1])
plt.show()
 

введите описание изображения здесь

Комментарии:

1. Спасибо за предоставление этого! Возможно, это не тождественно тому, что я искал, но это очень полезная альтернатива. Проведя еще немного исследований, я могу просто переключиться на R, потому что у них есть функция distGeo ( rdocumentation.org/packages/geosphere/versions/1.5-10/topics / … ), который, по-видимому, производит вычисления координат по оси x, которые я пытался выполнить.

2. На самом деле, теперь, когда я перешел к этому построчно, чтобы понять код, я действительно думаю, что это правильное решение!

3. @eric excellent, пожалуйста, примите в качестве ответа, нажав на галочку 🙂 Спасибо!