从CSV导入时间序列并使用Python绘制图形的最佳方法
我必须多次执行的一项任务是读取包含一些时间序列数据的CSV文件,然后绘制一个显示所有数据的图表 我必须从CSV文件导入数据。它可能没有排序,可能有间隙,每个系列可以在不同的日期开始和结束。例如:从CSV导入时间序列并使用Python绘制图形的最佳方法,python,perl,Python,Perl,我必须多次执行的一项任务是读取包含一些时间序列数据的CSV文件,然后绘制一个显示所有数据的图表 我必须从CSV文件导入数据。它可能没有排序,可能有间隙,每个系列可以在不同的日期开始和结束。例如: Employee;Year;Salary Mark;2014;29000 Paul;2013;33000 Paul;2014;34000 Mark;2011;20000 Mark;2012;24000 Mark;2015;30000 我想把它全部放在一个图表中,显示每个时间序列(两行,其中X轴是日期,
Employee;Year;Salary
Mark;2014;29000
Paul;2013;33000
Paul;2014;34000
Mark;2011;20000
Mark;2012;24000
Mark;2015;30000
我想把它全部放在一个图表中,显示每个时间序列(两行,其中X轴是日期,Y轴是员工的工资)。关键是要考虑到数据中存在的任何差距
好的,我开始学习用Perl编程。我解决这个问题的第一个方法是:
首先,从CSV导入数据并将其存储到哈希中,如下所示:
$imported_data->{$employee}{$year} = $salary;
[ 2011, 2012, 2013, 2014, 2015 ]
导入数据后,我使用循环和DateTime模块生成一系列无间隙的排序日期。它将产生如下数组:
$imported_data->{$employee}{$year} = $salary;
[ 2011, 2012, 2013, 2014, 2015 ]
然后,我为每个员工生成工资数据数组。我使用日期数组进行循环,查找每个日期的工资,如果数据缺失,则返回一个undef:
for my $date (@dates) {
$salary = $imported_data->{$employee}{$year};
if ( defined $salary ) {
push @salary_array, $salary;
} else {
push @salary_array, undef;
}
}
这将产生以下数组(每个员工一个):
现在我可以绘制数据,将日期数组(X值)与每个工资数组(Y值)配对,得到一个包含两个系列的XY图。数据现在已排序,间隙将正确显示
好的,我不知道这是否是最好的方法(可能不是…),但效果很好
然而,现在我开始使用Phyton,我想重新思考这种完成任务的方式。如您所见,在Perl中,我使用了散列来存储导入的数据。我不知道Phyton的字典是否能做到这一点,也不知道它是否易于管理
所以我愿意接受更有经验的程序员的想法。你会怎么做?对于这类事情有什么有用的模块吗?因为你是python新手,我建议你看看
numpy
和pandas
了解一般的数学和数据操作,看看matplotlib
了解绘图
对于这个问题,一个可能的解决方案是:
from collections import defaultdict
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
data = [["Mark", 2014, 29000],
["Paul", 2013, 33000],
["Paul", 2014, 34000],
["Mark", 2011, 20000],
["Mark", 2012, 24000],
["Mark", 2015, 30000]]
data_csv = pd.DataFrame(data, columns=["Employee", "Year", "Salary"])
data_dict = defaultdict(list)
for row in data_csv.values:
data_dict[row[0]].append(row[1:])
fig = plt.figure()
ax = plt.gca()
for name in data_dict.keys():
data_dict[name] = np.concatenate(data_dict[name]).reshape(-1, 2)
ax.scatter(data_dict[name][:,0], data_dict[name][:,1], label=name)
plt.legend()
plt.show()
在第一行中,我使用pandas
创建了一个虚拟csv文件,而在您的示例中,您将使用pd.read\u csv
加载它
核心是
defaultdict
:由于事先不知道员工的数量,字典的值被初始化为列表。然后我检查csv中的每一行,并将每个姓名(dict中的一个键)的年份和工资附加到列表中。此时,只需使用numpy
重塑2d数组中的所有内容并绘制结果。这里是另一个示例,使用numpy
、defaultdict
、csv.reader()
,以及类:
from collections import defaultdict
import csv
import matplotlib.pyplot as plt
import numpy as np
def main():
raw_data = FileReader(fn='salary.csv')
data = ProcessData(raw_data)
Plotter(data)
class ProcessData:
def __init__(self, data):
self.data = data
self.t = sorted(data.years, key=int)
self.names = sorted(list(data.salary.keys()))
self.salary = dict()
self.create_salary_data()
def create_salary_data(self):
for name in self.names:
s = []
for year in self.t:
if year in self.data.salary[name]:
s.append(self.data.salary[name][year])
else:
s.append(None)
self.salary[name] = s
class FileReader:
def __init__(self, **kwargs):
if not 'fn' in kwargs:
raise Exception('No file name given')
self.fn = kwargs['fn']
self.salary = defaultdict(lambda: defaultdict(float))
self.years = set()
self.read_file()
def read_file(self):
with open('salary.csv', newline='') as csvfile:
reader = csv.reader(csvfile, delimiter=';')
header = next(reader)
for row in reader:
if len(row)==0:
continue
if len(row) != 3:
raise Exception('Bad row length in csv file')
name, year, salary = row
self.salary[name][year] = salary
self.years.add(year)
class Plotter:
def __init__(self, data):
self.data = data
t = data.t
names = sorted(list(data.salary.keys()))
fig, ax = plt.subplots()
for name in names:
temp = data.salary[name]
s = list(map(lambda ss: np.nan if ss is None else float(ss), temp))
ax.plot(t, s, label=name)
ax.set(xlabel='Date (year)', ylabel='Salary (\$\$)',
title='Employee salary')
ax.grid()
ax.legend(loc='upper right')
plt.show()
main()