Matplotlib: рисование линий между точками, игнорирующими отсутствующие данные
У меня есть набор данных, которые я хочу построить в виде линейного графика. Для каждой серии некоторые данные отсутствуют (но разные для каждой серии). В настоящее время matplotlib не рисует строки, пропускающие отсутствующие данные: например
import matplotlib.pyplot as plt
xs = range(8)
series1 = [1, 3, 3, None, None, 5, 8, 9]
series2 = [2, None, 5, None, 4, None, 3, 2]
plt.plot(xs, series1, linestyle='-', marker='o')
plt.plot(xs, series2, linestyle='-', marker='o')
plt.show()
приводит к графику с зазорами в линиях. Как я могу сказать matplotlib, чтобы нарисовать линии через промежутки? (Я бы предпочел не интерполировать данные).
Ответы
Ответ 1
Вы можете маскировать значения NaN таким образом:
import numpy as np
import matplotlib.pyplot as plt
xs = np.arange(8)
series1 = np.array([1, 3, 3, None, None, 5, 8, 9]).astype(np.double)
s1mask = np.isfinite(series1)
series2 = np.array([2, None, 5, None, 4, None, 3, 2]).astype(np.double)
s2mask = np.isfinite(series2)
plt.plot(xs[s1mask], series1[s1mask], linestyle='-', marker='o')
plt.plot(xs[s2mask], series2[s2mask], linestyle='-', marker='o')
plt.show()
Это приводит к
![Plot]()
Ответ 2
Qouting @Rutger Kassies (ссылка):
Matplotlib только рисует линию между последовательными (действительными) точками данных, и оставляет зазор при значениях NaN.
Решение, если вы используете Pandas,:
#pd.Series
s.dropna().plot() #masking (as @Thorsten Kranz suggestion)
#pd.DataFrame
df['a_col_ffill'] = df['a_col'].ffill(method='ffill')
df['b_col_ffill'] = df['b_col'].ffill(method='ffill') # changed from a to b
df[['a_col_ffill','b_col_ffill']].plot()
Ответ 3
Без интерполяции вам нужно удалить None из данных. Это также означает, что вам нужно будет удалить значения X, соответствующие None в серии. Здесь (уродливый) один вкладыш для этого:
x1Clean,series1Clean = zip(* filter( lambda x: x[1] is not None , zip(xs,series1) ))
Функция лямбда возвращает False для значений None, фильтруя пары x, series из списка, затем повторно заносит данные обратно в исходную форму.
Ответ 4
Для чего, возможно, стоит, после некоторых проб и ошибок, я хотел бы добавить одно разъяснение к решению Thorsten. Надеемся, что сэкономить время для пользователей, которые искали в другом месте, пробовав этот подход.
Мне не удалось добиться успеха с одинаковой проблемой при использовании
from pyplot import *
и попытка построить с помощью
plot(abscissa[mask],ordinate[mask])
Казалось, что требуется использовать import matplotlib.pyplot as plt
, чтобы получить правильную обработку NaNs, хотя я не могу сказать, почему.
Ответ 5
Решение с пандами:
import matplotlib.pyplot as plt
import pandas as pd
def splitSerToArr(ser):
return [ser.index, ser.as_matrix()]
xs = range(8)
series1 = [1, 3, 3, None, None, 5, 8, 9]
series2 = [2, None, 5, None, 4, None, 3, 2]
s1 = pd.Series(series1, index=xs)
s2 = pd.Series(series2, index=xs)
plt.plot( *splitSerToArr(s1.dropna()), linestyle='-', marker='o')
plt.plot( *splitSerToArr(s2.dropna()), linestyle='-', marker='o')
plt.show()
Функция splitSerToArr
очень удобна при построении в Pandas. Это результат: ![enter image description here]()
Ответ 6
Возможно, я пропустил этот момент, но я верю, что Pandas теперь делает это автоматически. Приведенный ниже пример немного связан и требует доступа в Интернет, но линия для Китая имеет много пробелов в первые годы, следовательно, прямые сегменты.
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
# read data from Maddison project
url = 'http://www.ggdc.net/maddison/maddison-project/data/mpd_2013-01.xlsx'
mpd = pd.read_excel(url, skiprows=2, index_col=0, na_values=[' '])
mpd.columns = map(str.rstrip, mpd.columns)
# select countries
countries = ['England/GB/UK', 'USA', 'Japan', 'China', 'India', 'Argentina']
mpd = mpd[countries].dropna()
mpd = mpd.rename(columns={'England/GB/UK': 'UK'})
mpd = np.log(mpd)/np.log(2) # convert to log2
# plots
ax = mpd.plot(lw=2)
ax.set_title('GDP per person', fontsize=14, loc='left')
ax.set_ylabel('GDP Per Capita (1990 USD, log2 scale)')
ax.legend(loc='upper left', fontsize=10, handlelength=2, labelspacing=0.15)
fig = ax.get_figure()
fig.show()