使用 Pandas 检查数据
您不需要成为数据科学家也能使用 Pandas 进行一些基本分析。
传统上,使用 Python 编程的人员使用该语言自带的数据类型,例如整数、字符串、列表、元组和字典。当然,您可以在 Python 中创建对象,但这些对象通常是基于这些基本数据结构构建的。
如果您是一位使用 Pandas 的数据科学家,那么您的大部分时间都花在了 NumPy 上。NumPy 可能感觉像是 Python 数据结构,但它在很多方面的行为都不同。这不仅是因为它的所有操作都通过向量工作,还因为底层数据实际上是一个 C 风格的数组。这使得 NumPy 非常快速和高效,对于给定的数字数组,它比传统的 Python 对象消耗的内存要少得多。
问题是,NumPy 的设计目的是快速,但对于某些人来说,它的级别也有些低。为了获得更多的功能和更灵活的接口,许多人使用 Pandas,这是一个 Python 包,它围绕 NumPy 数组提供了两个基本的包装器:一维 Series 对象和二维 Data Frame 对象。
我经常将 Pandas 描述为“Python 中的 Excel”,因为您可以使用它执行各种计算,以及对数据进行排序、搜索和绘制图表。
由于所有这些原因,Pandas 成为数据科学界的宠儿也就不足为奇了。但问题是:您不需要成为数据科学家也能享受 Pandas 的乐趣。它有很多出色的功能,对于那些原本会花费时间与列表、元组和字典搏斗的 Python 开发人员来说非常有用。
因此,在本文中,我将描述一些每个人都可以使用 Pandas 进行的基本分析,无论您是否是数据科学家。如果您曾经使用过 CSV 文件(您可能经常使用),我绝对建议您考虑使用 Pandas 来打开、读取、分析甚至写入它们。虽然我在本文中没有介绍,但 Pandas 也很好地处理 JSON 和 Excel。
创建数据帧虽然可以从头开始使用 Python 数据结构或 NumPy 数组创建数据帧,但在我的经验中,更常见的是从文件创建。幸运的是,Pandas 可以从各种文件格式加载数据。
在您可以使用 Pandas 做任何事情之前,您必须加载它。在 Jupyter notebook 中,执行
%pylab inline
import pandas as pd
例如,Python 自带一个 csv
模块,它知道如何处理 CSV(逗号分隔值)格式的文件。但是,您需要遍历文件并对每一行/行执行一些操作。我经常发现使用 Pandas 处理此类文件更容易。例如,这是一个 CSV 文件
a,b,c,d
e,f,g,h
"i,j",k,l,m
n,o.p,q
您可以使用以下命令将其转换为数据帧
df = pd.read_csv('mycsv.csv')
现在您可以使用以下命令在 Jupyter 中查看数据帧的内容
df
问题是,您可能甚至没有意识到有各种各样的 CSV 文件。例如,考虑 Linux /etc/passwd 文件。它实际上不是 CSV 文件,但如果您仔细考虑一下,您会意识到格式是相同的。每个记录都在一行上,字段用“:”字符分隔。所以在这种情况下,您需要使用 sep
参数来指示分隔符
filename = '/etc/passwd'
df = pd.read_csv(filename, sep=':')
df.head()
命令 df.head()
,很像 UNIX head
实用程序,显示数据帧的前几行。我经常使用这种方法来检查数据并确保数据传输正常。
在这种特定情况下,数据传输得很好,但是第一行(root 用户的记录!)被解释为标题行。幸运的是,您可以使用另一个参数来修复它
df = pd.read_csv(filename, sep=':', header=None)
df.head()
如果您传递 header
参数,您就是在告诉 Pandas 文件中的哪一行应该被视为标题。但是,如果您传递 None
作为值,您就是在告诉 Pandas 没有哪一行是标题。这没关系,但是您将获得整数(从 0 开始)作为列名。我宁愿使用真实名称,所以要指定它们,请执行
df = pd.read_csv(filename, sep=':', header=None,
names=['username', 'password', 'uid',
'gid', 'name', 'homedir', 'shell'])
df.head()
问题是,您真的需要 password
列吗?考虑到在现代计算机上,它总是包含“x”,我认为您可以将其省略。所以,您可以说
df = pd.read_csv(filename, sep=':', header=None,
usecols=[0,2,3,4,5,6],
names=['username', 'uid', 'gid',
'name', 'homedir', 'shell'])
df.head()
usecols
参数指示您要从文件中读取哪些列。
现在,您为什么要对 /etc/passwd 文件使用 Pandas 呢?您可能不会,但您可以想象,如果它适用于该文件,它也适用于其他文件。
例如,现在许多城市都在向公众提供数据。芝加哥在线发布了许多数据,所以让我们看一下,例如,您可以了解芝加哥过去 90 天内被拖走的汽车的信息。
您可以访问芝加哥数据门户网站 https://data.cityofchicago.org/browse。我点击了“towed vehicles”→“export”→“CSV”以获取 CSV 文件。然后我获取下载的文件并将其导入到 Pandas 中,使用
df = pd.read_csv('towed.csv')
不,我只是在开玩笑。您认为我有时间和精力下载文件然后加载它吗?我最喜欢的 Pandas 功能之一是,大多数处理文件的方法也可以处理 URL。所以,我可以这样说
url = 'https://data.cityofchicago.org/api/views/ygr5-vcbg/
↪rows.csv?accessType=DOWNLOAD'
df = pd.read_csv(url)
(请注意,当您阅读本文时,URL 很可能已经更改,或者它可能与我的会话信息相关联。如果不是,从我的角度来看,那就更好了!)
然后我查看数据帧,发现它有标题,分隔符是逗号,并且工作正常。我唯一要做的调整是将“拖车日期”列解析为日期,这样我就可以稍微玩一下它。我还指示,由于日期是美国格式,因此日期在前
df = pd.read_csv(url, parse_dates=['Tow Date'], dayfirst=False)
df.head()
您会注意到,第一列现在看起来有点不同,以年-月-日格式显示。这表明有一个时间戳。您还可以看到,如果您运行 df.info
方法,它会告诉您有关数据帧本身的信息
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 5560 entries, 0 to 5559
Data columns (total 10 columns):
Tow Date 5560 non-null datetime64[ns]
Make 5537 non-null object
Style 5538 non-null object
Model 509 non-null object
Color 5536 non-null object
Plate 4811 non-null object
State 5392 non-null object
Towed to Address 5560 non-null object
Tow Facility Phone 5559 non-null object
Inventory Number 5560 non-null int64
dtypes: datetime64[ns](1), int64(1), object(8)
memory usage: 434.5+ KB
如您所见,“Tow Date”列现在存储为 datetime64
,这是一个包含日期和时间的 64 位字段。
所以,现在您有了这些数据,您可以用它做什么呢?我最喜欢的方法之一是 value_counts
,它可以告诉您特定值在列中出现的次数。因此,您可以说以下内容来找出每个州拖走了多少辆车
df['State'].value_counts()
现在,这将是一个很长的列表,因此最好使用 head
限制从 value_counts
返回的序列
df['State'].value_counts().head(10)
现在您将看到在过去 90 天内在芝加哥被拖走车辆最多的十个州
IL 4948
IN 148
TX 48
WI 48
MN 28
IA 27
MI 19
GA 14
FL 14
TN 13
毫不奇怪,在芝加哥被拖走的大多数车辆来自伊利诺伊州,其次是附近的印第安纳州。至少对我来说,不太令人意外的是来自德克萨斯州的被拖走车辆数量很多,德克萨斯州并不是很近。
为了利用时间戳现在是真正的 datetime
对象这一事实,您可以找出车辆被拖走最常见的日期
2018-03-03 215
2018-03-04 195
2018-02-24 165
2018-03-25 148
2018-03-26 140
2018-03-24 135
2018-03-15 126
2018-03-21 122
2018-02-03 120
2018-03-22 117
如您所见,存在很大的差异。也许这种差异是由于星期几造成的?为了弄清楚,您可以使用 Pandas 为 datetime
列提供的 dt
代理对象。您可以使用它从 datetime
列中提取信息,然后对其进行分析。例如,您可以说
df['Tow Date'].dt.dayofweek
这将检索每个拖车日期的星期几。然后您可以使用 value_counts
汇总每周中每天拖走了多少辆车
df['Tow Date'].dt.dayofweek.value_counts()
结果如下
5 1143
4 831
3 820
6 798
2 794
0 640
1 534
以上表明,芝加哥星期五拖走的汽车比其他任何一天都多得多。这可能一直如此,或者可能只是对于这个 90 天的样本而言如此。
您还可以检查哪些品牌的汽车最常被拖走,或者颜色与被拖走的几率之间是否存在关联。
如果您想知道每周拖走了多少辆车怎么办?为此,您可以利用 Pandas 的一个很棒的功能,您可以在其中将数据帧的索引设置为时间戳列
df.set_index('Tow Date', inplace=True)
现在,您将使用时间戳而不是整数索引来访问行。但重点不是以这种方式访问事物。相反,它是 resample
,类似于 SQL 中的 GROUP BY
查询。重采样可以以多种方式完成;这里要求它以一周为单位
df.resample('1W')
就其本身而言,这没有任何作用。但是,如果您随后计算每周的行数,您将获得更有趣的输出。对于每一列,您都会获得每周的条目数。数字有所不同,因为许多列都缺少 (NaN
) 信息,这些信息不包含在计数中。这没关系;您可以只使用 Make
列,该列似乎已为每辆被拖走的车辆填写
df.resample('1W').count()['Make']
现在您应该得到一份非常好的报告,显示每周拖走了多少辆汽车
Tow Date
2017-12-31 103
2018-01-07 321
2018-01-14 209
2018-01-21 241
2018-01-28 250
2018-02-04 399
2018-02-11 248
2018-02-18 328
2018-02-25 587
2018-03-04 862
2018-03-11 495
2018-03-18 601
2018-03-25 754
2018-04-01 139
Freq: W-SUN, Name: Make, dtype: int64
与数字表格相比,人类更容易看到图形中的模式,因此您可能需要绘制此图,也使用 Pandas
df.resample('1W').count()['Make'].plot()
这将创建一个折线图,显示 3 月初被拖走的车辆数量激增。为什么?您可以查看温度和日历开始猜测,但您可以如此轻松地下载、检查和绘制此信息是一个很好的起点。
结论Pandas 不仅仅是数据科学家进行数值分析的工具。它可以读取(和写入)CSV、Excel 和 JSON 等格式,这使其成为我处理这些格式的主要工具。再加上它可以汇总数据,包括基于时间戳的数据,您可能可以理解为什么 Pandas 如此受欢迎。
资源Pandas 的主页是 这里,我在本文中提到的 Jupyter notebook 是 这里。
最后,您可以通过 Pandas 中时间戳列上的 dt
对象访问的“datetime 组件”列表是 这里。