程序员清理杂乱传感器数据的指南

如果您以前从未使用过 Pandas,并且了解 Python 的基础知识,那么本教程非常适合您。
1046 位读者喜欢这篇文章。
CSV screenshot

Xavier Ho。CC BY-SA 4.0

在本教程中,我将解释如何使用 PandasPython 来处理杂乱的数据。如果您以前从未使用过 Pandas,并且了解 Python 的基础知识,那么本教程非常适合您。

天气数据是杂乱数据集的一个很好的真实示例。它带有混合内容、不规则的缺失值和时区,所有这些都是数据科学家常见的痛点。我将通过示例说明如何处理混合内容和不规则的缺失值。要处理奇怪的时区,请参阅 Mario Corchero 的优秀文章,如何使用 Python 处理日期和时间

让我们从头开始,将一个杂乱的文件转换为有用的数据集。完整源代码可在 GitHub 上获取

读取 CSV 文件

您可以使用以下代码在 Pandas 中打开 CSV 文件

  • pandas.read_csv():将 CSV 文件作为 DataFrame(类似于表格)打开。
  • DataFrame.head():显示前 5 个条目。

DataFrame 就像 Pandas 中的表格;它具有一组固定的列和索引。CSV 文件非常适合 DataFrame,因为它们以数据列和行的形式出现。

import pandas as pd
     
# Open a comma-separated values (CSV) file as a DataFrame
weather_observations = \  
  pd.read_csv('observations/Canberra_observations.csv')
     
# Print the first 5 entries
weather_observations.head()

Tab-separated data

opensource.com

看起来我们的数据实际上是由 \t 制表符分隔的。其中有一些有趣的项,看起来像是时间。

pandas.read_csv() 为不同的情况提供了通用的关键字参数。在这里,您有一列用于日期,另一列用于时间。您可以引入一些关键字参数来增加一些智能性

  • sep:列之间的分隔符
  • parse_dates:将一列或多列视为日期
  • dayfirst:使用 DD.MM.YYYY 格式,而不是月份优先
  • infer_datetime_format:告诉 Pandas 猜测日期格式
  • na_values:添加要视为空的值

使用这些关键字参数预先格式化数据,让 Pandas 完成一些繁重的工作。

# Supply pandas with some hints about the file to read
weather_observations = \
  pd.read_csv('observations/Canberra_observations.csv',
     sep='\t',
     parse_dates={'Datetime': ['Date', 'Time']},
     dayfirst=True,
     infer_datetime_format=True,
     na_values=['-']
)

Table

Pandas 巧妙地将 DateTime 两列转换为单个列 Datetime,并以标准格式呈现。

这里有一个 NaN 值,不要与“非数字”浮点数混淆。这只是 Pandas 表示它是空的方式。

按顺序排序数据

让我们看看 Pandas 可以处理数据顺序的方法。

  • DataFrame.sort_values():按顺序重新排列。
  • DataFrame.drop_duplicates():删除重复项。
  • DataFrame.set_index():指定要用作索引的列。

因为时间似乎在倒退,所以让我们对其进行排序

# Sorting is ascending by default, or chronological order
sorted_dataframe = weather_observations.sort_values('Datetime')
sorted_dataframe.head()

Two midnights

为什么会有两个午夜?事实证明,我们的数据集(原始数据)包含每天结束和开始时的午夜。您可以丢弃一个作为重复项,因为第二天也会有另一个午夜。

这里的逻辑顺序是丢弃重复项,对数据进行排序,然后设置索引

# Sorting is ascending by default, or chronological order
sorted_dataframe = weather_observations.sort_values('Datetime')

# Remove duplicated items with the same date and time
no_duplicates = sorted_dataframe.drop_duplicates('Datetime', keep='last')

# Use `Datetime` as our DataFrame index
indexed_weather_observations = \
  sorted_dataframe.set_index('Datetime')
indexed_weather_observations.head()

Sorted data

opensource.com

现在您有了一个以时间为索引的 DataFrame,这在以后会很有用。首先,让我们转换风向。

转换列值

为了准备用于天气建模的风数据,您可以使用数值格式的风值。按照惯例,北风 (↓) 为 0 度,顺时针方向 ⟳。东风 (←) 为 90 度,依此类推。您将利用 Pandas 进行转换

  • Series.apply():使用函数转换每个条目。

为了计算出每个风向的确切值,我手动编写了一个字典,因为只有 16 个值。这既简洁又易于理解。

# Translate wind direction to degrees
wind_directions = {
     'N':   0. , 'NNE':  22.5, 'NE':  45. , 'ENE':  67.5 ,
     'E':  90. , 'ESE': 112.5, 'SE': 135. , 'SSE': 157.5 ,
     'S': 180. , 'SSW': 202.5, 'SW': 225. , 'WSW': 247.5 ,
     'W': 270. , 'WNW': 292.5, 'NW': 315. , 'NNW': 337.5 }

您可以像使用 Python 字典一样,通过索引访问器访问 DataFrame 列,在 Pandas 中称为 Series。转换后,Series 将被新值替换。

# Replace wind directions column with a new number column
# `get()` accesses values fomr the dictionary safely
indexed_weather_observations['Wind dir'] = \
    indexed_weather_observations['Wind dir'].apply(wind_directions.get)

# Display some entries
indexed_weather_observations.head()

Transformed

opensource.com

每个有效的风向现在都是一个数字。值是字符串还是另一种数字都没有关系;您可以使用 Series.apply() 来转换它。

设置索引频率

深入挖掘,您会在数据集中发现更多缺陷

# One section where the data has weird timestamps ...
indexed_weather_observations[1800:1805]

Table

opensource.com

00:33:0001:11:00?这些是奇怪的时间戳。有一个函数可以确保一致的频率

DataFrame.asfreq():强制索引具有特定频率,丢弃并填充其余部分。

# Force the index to be every 30 minutes
regular_observations = \
  indexed_weather_observations.asfreq('30min')
         
# Same section at different indices since setting  
# its frequency :)
regular_observations[1633:1638]

Table

opensource.com

Pandas 丢弃任何与频率不匹配的索引,并在不存在索引时添加空行。现在您有了一致的索引频率。让我们用 matplotlib(一个流行的绘图库)绘制它,看看它是什么样子

import matplotlib.pyplot as plt

# Make the graphs a bit prettier
pd.set_option('display.mpl_style', 'default') 
plt.rcParams['figure.figsize'] = (18, 5)

# Plot the first 500 entries with selected columns
regular_observations[['Wind spd', 'Wind gust', 'Tmp', 'Feels like']][:500].plot()

Chart

opensource.com

仔细观察,似乎在 1 月 6 日、7 日左右以及更多地方存在空白。您需要用有意义的内容填充这些空白。

插值和填充空行

为了填充空白,您可以线性插值这些值,或者从空白的两个端点绘制一条线,并相应地填充每个时间戳。

  • Series.interpolate():根据索引填充空值。

在这里,您还使用 inplace 关键字参数来告诉 Pandas 执行操作并替换自身。

# Interpolate data to fill empty values
for column in regular_observations.columns:
    regular_observations[column].interpolate('time', inplace=True, limit_direction='both')

# Display some interpolated entries    
regular_observations[1633:1638]

opensource.com

NaN 值已被替换。让我们再次绘制它

# Plot it again - gap free!
regular_observations[['Wind spd', 'Wind gust', 'Tmp', 'Feels like']][:500].plot()

Charts

opensource.com

恭喜!数据现在已准备好用于天气处理。您可以在 GitHub 上下载示例代码并进行尝试。

结论

我已经展示了如何通过多种方式使用 Python 和 Pandas 清理杂乱的数据,例如

  • 使用正确的结构读取 CSV 文件,
  • 对数据集进行排序,
  • 通过应用函数转换列
  • 规范数据频率
  • 插值和填充缺失数据
  • 绘制数据集

Pandas 提供了更多强大的功能,您可以在文档及其优秀的10 分钟入门中找到。您可能会在其中找到一些宝藏。如果您有任何问题或想法,请随时通过 Twitter 联系我 @Xavier_Ho

祝您数据清理愉快!

更多资源

Xavier Ho
Xavier 是一位好奇心驱动的设计师、研究员和软件工程师。他曾在悉尼一家初创公司从事计算机视觉工作,做过摄像师自由职业者,并为大学设计专业的学生教授了一些编程课程。他的热情在于巧克力、视频游戏和更美好的世界的某个领域。

3 条评论

非常好的文章!!我开始明白人们为什么喜欢 Python 了。我还没有喜欢上它,但我正在接近。

您好,我是 InfoQ 中国站点的编辑。我们可以将您的文章翻译成中文并发布到 InfoQ 中国站点吗?我们将提供返回到这篇原始文章的链接。谢谢。

嗨 Carol,可以的。请在链接上线后告诉我,这样我就可以在 Twitter 上分享了!

回复 ,作者 CarolGuo (未验证)

© . All rights reserved.