График, если данные доступны в любой момент для каждой станции, один график - PullRequest
0 голосов
/ 19 апреля 2019

Как следует из названия, я хотел бы построить график доступности данных в любое время для каждой станции. Сюжет можно считать картой или точечной диаграммой, где номер станции и время являются координатами. Который будет строить вертикальные линии, где есть данные (то есть, с плавающей запятой / целыми числами), и в виде пробела, если данные отсутствуют (т. Е. NAN), временное разрешение - ежедневно.

Похоже на сюжет в конце поста. Который из вывода пакета R, «Climatol» (функция гомогена).

Я хотел бы знать, есть ли подобный способ построения графиков в PYTHON, я предпочтительно не хочу использовать пакет R, поскольку он делает больше, чем просто график, и, следовательно, займет тысячи часов для тысяч данные станции.

Некоторые выборочные данные (дневные временные ряды) каждой станции будут выглядеть так:

station1 = pd.DataFrame(pd.np.random.rand(100, 1)).set_index(pd.date_range(start = '2000/01/01', periods = 100))
station2 = pd.DataFrame(pd.np.random.rand(200, 1)).set_index(pd.date_range(start = '2000/03/01', periods = 200))
station3 = pd.DataFrame(pd.np.random.rand(300, 1)).set_index(pd.date_range(start = '2000/06/01', periods = 300))
station4 = pd.DataFrame(pd.np.random.rand(50, 1)).set_index(pd.date_range(start = '2000/09/01', periods = 50))
station5 = pd.DataFrame(pd.np.random.rand(340, 1)).set_index(pd.date_range(start = '2000/01/01', periods = 340))

Реальные образцы данных; https://drive.google.com/drive/folders/15PwpWIh13tyOyzFUTiE9LgrxUMm-9gh6?usp=sharing Код для открытия на две станции;

import pandas as pd
import numpy as np


df1 = pd.read_csv('wgenf - 2019-04-17T012724.318.genform1_proc',skiprows = 8,delimiter = '  ')
df1.drop(df1.tail(6).index,inplace=True)
df1 = df1.iloc[:,[1,3]]
df1.iloc[:,1].replace('-',np.nan,inplace=True)
df1 = df1.dropna()
df1['Date(NZST)'] = pd.to_datetime(df1.iloc[:,0],format = "%Y %m %d")
df1 = df1.set_index('Date(NZST)')

df2 = pd.read_csv('wgenf - 2019-04-17T012830.116.genform1_proc',skiprows = 8,delimiter = '  ')
df2.drop(df2.tail(6).index,inplace=True)
df2 = df2.iloc[:,[1,3]]
df2.iloc[:,1].replace('-',np.nan,inplace=True)
df2 = df2.dropna()
df2['Date(NZST)'] = pd.to_datetime(df2.iloc[:,0],format = "%Y %m %d")
df2 = df2.set_index('Date(NZST)')

enter image description here

Расширение кода Асмуса (ответ ниже) для нескольких станций

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt 
import glob as glob
start = '1900/01/01'
end = '2018/12/31'
counter = 0
filenames = glob.glob('data/temperature/*.genform1_proc')
for filename in filenames:
    with open(filename, newline='') as f:

        ### read the csv file with pandas, using the correct tab delimiter 
        df1 = pd.read_csv(f,skiprows = 8,delimiter = '\t',)
        df1.drop(df1.tail(8).index,inplace=True)


        ### replace invalid '-' with useable np.nan (not a number)
        df1.replace('-',np.nan,inplace=True)
        df1['Date(NZST)'] = pd.to_datetime(df1['Date(NZST)'],format = "%Y %m %d")
        df1 = df1.set_index('Date(NZST)',drop=False)

        ### To make sure that we have data on all dates:
        #   create a new index, based on the old range, but daily frequency
        idx = pd.date_range(start,end,freq="D")
        df1=df1.reindex(idx, fill_value=np.nan)

        ### Make sure interesting data fields are numeric (i.e. floats)
        df1["Tmax(C)"]=pd.to_numeric(df1["Tmax(C)"])
        ### Create masks for 
        #   valid data: has both date and temperature
        valid_mask= df1['Tmax(C)'].notnull()

        ### decide where to plot the line in y space, 
        ys=[counter for v in df1['Tmax(C)'][valid_mask].values]


        plt.scatter(df1.index[valid_mask].values,ys,s=30,marker="|",color="g")
        plt.show()

        counter +=1

Код выше в настоящее время отображает тот, что ниже

enter image description here

1 Ответ

1 голос
/ 19 апреля 2019

Обновлено : Я обновил этот ответ в соответствии с комментариями

Хорошо, поэтому, во-первых, ваши входные данные немного запутаны, с разделителем, фактически являющимся табуляцией ('\t'), а первый столбец скорее заканчивается на ,.

Важные шаги:

  • сначала позаботьтесь об очистке, заменив , на \t и, таким образом, убедитесь, что заголовки столбцов правильно прочитаны как df.keys(). Хотя вы можете думать, что это не важно, старайтесь содержать вещи в чистоте! : -)
  • столбец индекса «Дата (NZST)» сохраняется как столбец, и создается новый столбец индекса (idx), который содержит все дни в данном диапазоне, поскольку некоторые дни отсутствуют в исходных данных.
  • убедитесь, что соответствующие ключи / столбцы имеют соответствующий тип, например «Tmax (C)» должен быть плавающим.
  • наконец, вы можете использовать .notnull(), чтобы получить только действительные данные, но убедитесь, что присутствуют и дата и температура! Это сохраняется как valid_mask для простоты использования

В конце я построил данные, используя зеленые вертикальные линии в качестве маркеров для «достоверных» измерений и то же самое в красном для недействительных данных. Смотрите рисунок. Теперь вам нужно только запустить это для всех станций. Надеюсь, это поможет!

sample plot of valid / invalid data

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt 
from io import StringIO
import re
fpath='./wgenf - 2019-04-17T012537.711.genform1_proc'

### cleanup the input file
for_pd = StringIO()
with open(fpath) as fi:
    for line in fi:
        new_line = re.sub(r',', '\t', line.rstrip(),)
        print (new_line, file=for_pd)

for_pd.seek(0)

### read the csv file with pandas, using the correct tab delimiter 
df1 = pd.read_csv(for_pd,skiprows = 8,delimiter = '\t',)
df1.drop(df1.tail(6).index,inplace=True)

### replace invalid '-' with useable np.nan (not a number)
df1.replace('-',np.nan,inplace=True)
df1['Date(NZST)'] = pd.to_datetime(df1['Date(NZST)'],format = "%Y %m %d")
df1 = df1.set_index('Date(NZST)',drop=False)

### To make sure that we have data on all dates:
#   create a new index, based on the old range, but daily frequency
idx = pd.date_range(df1.index.min(), df1.index.max(),freq="D")
df1=df1.reindex(idx, fill_value=np.nan)

### Make sure interesting data fields are numeric (i.e. floats)
df1["Tmax(C)"]=pd.to_numeric(df1["Tmax(C)"])
df1["Station"]=pd.to_numeric(df1["Station"])

### Create masks for 
#   invalid data: has no date, or no temperature
#   valid data: has both date and temperature
valid_mask=( (df1['Date(NZST)'].notnull()) & (df1['Tmax(C)'].notnull()))
na_mask=( (df1['Date(NZST)'].isnull()) & (df1['Tmax(C)'].isnull()))


### Make the plot
fig,ax=plt.subplots()

### decide where to plot the line in y space, here: "1"
ys=[1 for v in df1['Station'][valid_mask].values]
### and plot the data, using a green, vertical line as marker
ax.scatter(df1.index[valid_mask].values,ys,s=10**2,marker="|",color="g")

### potentially: also plot the missing data, using a re, vertical line as marker at y=0.9
yerr=[0.9 for v in df1['Station'][na_mask].values]
ax.scatter(df1.index[na_mask].values,yerr,s=10**2,marker="|",color="r")

### set some limits on the y-axis
ax.set_ylim(0,2)

plt.show()
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...