Python

Python 数据处理(十五)

2021-02-17  本文已影响0人  名本无名

9 日期处理

9.1 指定日期列

为了更好地处理 datetime 数据,read_csv() 提供了 parse_datesdate_parser 关键字参数来指定日期/时间格式,以便将输入文本数据转换为 datetime 对象

最简单的一种情况是只传递 parse_dates=True

# Use a column as an index, and parse it as dates.
In [97]: df = pd.read_csv("foo.csv", index_col=0, parse_dates=True)

In [98]: df
Out[98]: 
            A  B  C
date               
2009-01-01  a  1  2
2009-01-02  b  3  4
2009-01-03  c  4  5

# These are Python datetime objects
In [99]: df.index
Out[99]: DatetimeIndex(['2009-01-01', '2009-01-02', '2009-01-03'], dtype='datetime64[ns]', name='date', freq=None)

通常情况下,我们可能希望单独存储日期和时间数据,或者单独存储各种日期字段。

parse_dates 参数可用于指定要从中解析日期和/或时间的列组合

您可以为 parse_dates 指定一组列,组合后的日期列将被添加到输出结果的前面(为了不影响现有的列顺序),新的列名将是以 _ 连接的列名

In [100]: print(open("tmp.csv").read())
KORD,19990127, 19:00:00, 18:56:00, 0.8100
KORD,19990127, 20:00:00, 19:56:00, 0.0100
KORD,19990127, 21:00:00, 20:56:00, -0.5900
KORD,19990127, 21:00:00, 21:18:00, -0.9900
KORD,19990127, 22:00:00, 21:56:00, -0.5900
KORD,19990127, 23:00:00, 22:56:00, -0.5900

In [101]: df = pd.read_csv("tmp.csv", header=None, parse_dates=[[1, 2], [1, 3]])

In [102]: df
Out[102]: 
                  1_2                 1_3     0     4
0 1999-01-27 19:00:00 1999-01-27 18:56:00  KORD  0.81
1 1999-01-27 20:00:00 1999-01-27 19:56:00  KORD  0.01
2 1999-01-27 21:00:00 1999-01-27 20:56:00  KORD -0.59
3 1999-01-27 21:00:00 1999-01-27 21:18:00  KORD -0.99
4 1999-01-27 22:00:00 1999-01-27 21:56:00  KORD -0.59
5 1999-01-27 23:00:00 1999-01-27 22:56:00  KORD -0.59

默认情况下,解析器会删除组合前的日期对应的列,但是您可以选择通过 keep_date_col 关键字保留它们

In [103]: df = pd.read_csv(
   .....:     "tmp.csv", header=None, parse_dates=[[1, 2], [1, 3]], keep_date_col=True
   .....: )
   .....: 

In [104]: df
Out[104]: 
                  1_2                 1_3     0         1          2          3     4
0 1999-01-27 19:00:00 1999-01-27 18:56:00  KORD  19990127   19:00:00   18:56:00  0.81
1 1999-01-27 20:00:00 1999-01-27 19:56:00  KORD  19990127   20:00:00   19:56:00  0.01
2 1999-01-27 21:00:00 1999-01-27 20:56:00  KORD  19990127   21:00:00   20:56:00 -0.59
3 1999-01-27 21:00:00 1999-01-27 21:18:00  KORD  19990127   21:00:00   21:18:00 -0.99
4 1999-01-27 22:00:00 1999-01-27 21:56:00  KORD  19990127   22:00:00   21:56:00 -0.59
5 1999-01-27 23:00:00 1999-01-27 22:56:00  KORD  19990127   23:00:00   22:56:00 -0.59

注意,如果您希望将多个列合并为一个日期列,则必须使用嵌套列表。

也就是说,parse_dates=[1,2] 表示第二和第三列应该分别解析为单独的日期列,而 parse_dates=[[1,2]] 表示这两列应该解析为一个单独的列

你也可以使用字典来自定义组合的名称列

In [105]: date_spec = {"nominal": [1, 2], "actual": [1, 3]}

In [106]: df = pd.read_csv("tmp.csv", header=None, parse_dates=date_spec)

In [107]: df
Out[107]: 
              nominal              actual     0     4
0 1999-01-27 19:00:00 1999-01-27 18:56:00  KORD  0.81
1 1999-01-27 20:00:00 1999-01-27 19:56:00  KORD  0.01
2 1999-01-27 21:00:00 1999-01-27 20:56:00  KORD -0.59
3 1999-01-27 21:00:00 1999-01-27 21:18:00  KORD -0.99
4 1999-01-27 22:00:00 1999-01-27 21:56:00  KORD -0.59
5 1999-01-27 23:00:00 1999-01-27 22:56:00  KORD -0.59

如果要将多个文本列解析为单个日期列,那么数据前面会有一个新列。

index_col 是基于这组新的列而不是原始的数据列

In [108]: date_spec = {"nominal": [1, 2], "actual": [1, 3]}

In [109]: df = pd.read_csv(
   .....:     "tmp.csv", header=None, parse_dates=date_spec, index_col=0
   .....: )  # index is the nominal column
   .....: 

In [110]: df
Out[110]: 
                                 actual     0     4
nominal                                            
1999-01-27 19:00:00 1999-01-27 18:56:00  KORD  0.81
1999-01-27 20:00:00 1999-01-27 19:56:00  KORD  0.01
1999-01-27 21:00:00 1999-01-27 20:56:00  KORD -0.59
1999-01-27 21:00:00 1999-01-27 21:18:00  KORD -0.99
1999-01-27 22:00:00 1999-01-27 21:56:00  KORD -0.59
1999-01-27 23:00:00 1999-01-27 22:56:00  KORD -0.59

注意
如果列或索引包含无法解析的日期,则整个列或索引将不变地作为 object 数据类型返回。

对于非标准日期时间解析,请在 pd.read_csv 之后使用 to_datetime() 解析

9.2 日期解析函数

解析器允许您指定一个自定义的 date_parser 函数,以充分利用日期解析 API 的灵活性

In [111]: df = pd.read_csv(
   .....:     "tmp.csv", header=None, parse_dates=date_spec, date_parser=pd.to_datetime
   .....: )
   .....: 

In [112]: df
Out[112]: 
              nominal              actual     0     4
0 1999-01-27 19:00:00 1999-01-27 18:56:00  KORD  0.81
1 1999-01-27 20:00:00 1999-01-27 19:56:00  KORD  0.01
2 1999-01-27 21:00:00 1999-01-27 20:56:00  KORD -0.59
3 1999-01-27 21:00:00 1999-01-27 21:18:00  KORD -0.99
4 1999-01-27 22:00:00 1999-01-27 21:56:00  KORD -0.59
5 1999-01-27 23:00:00 1999-01-27 22:56:00  KORD -0.59

pandas 将尝试以三种不同的方式调用 date_parser 函数。如果抛出异常,则尝试下一种方式

  1. date_parser 首先使用一个或多个数组作为参数被调用,这些数组是使用 parse_dates 定义的(例如,date_parser(['2013', '2013'], ['1', '2'])

  2. 如果 1 失败了,则会调用 date_parser ,并将所有列按行连接到单个数组中(例如,date_parser(['2013 1','2013 2'])

注意,在性能方面,你应该尝试按以下顺序来解析日期

  1. 尝试使用 infer_datetime_format=True 来推断格式
  2. 如果你知道格式,可以使用 pd.to_datetime(): date_parser=lambda x: pd.to_datetime(x, format=...)
  3. 如果是非标准时间日期格式,请使用自定义 date_parser 函数
9.3 解析带有混合时区的 CSV

pandas 本身不能表示带有混合时区的列或索引。如果 CSV 文件包含混合时区的列,则默认结果将是一个字符串类型的列,即使使用了 parse_dates 也一样

In [113]: content = """\
   .....: a
   .....: 2000-01-01T00:00:00+05:00
   .....: 2000-01-01T00:00:00+06:00"""
   .....: 

In [114]: df = pd.read_csv(StringIO(content), parse_dates=["a"])

In [115]: df["a"]
Out[115]: 
0    2000-01-01 00:00:00+05:00
1    2000-01-01 00:00:00+06:00
Name: a, dtype: object

要将混合时区值解析为 datetime 类型,需要传递一个部分应用的 to_datetime(),且设置参数 utc=True 作为 date_parser

In [116]: df = pd.read_csv(
   .....:     StringIO(content),
   .....:     parse_dates=["a"],
   .....:     date_parser=lambda col: pd.to_datetime(col, utc=True),
   .....: )
   .....: 

In [117]: df["a"]
Out[117]: 
0   1999-12-31 19:00:00+00:00
1   1999-12-31 18:00:00+00:00
Name: a, dtype: datetime64[ns, UTC]
9.4 推断 datetime 格式

如果您为部分或所有列启用了 parse_dates,并且 datetime 字符串都是相同的格式,那么通过设置 infer_datetime_format=True,可以大大提升解析速度。

如果无法猜测格式,或者猜测的格式不能正确解析整个字符串列,将会使用通用的解析方式

下面是一些可以猜测的 datetime 字符串的例子(都表示 2011123000:00:00)

注意infer_datetime_formatdayfirst 敏感。如果 dayfirst=True,它就会把 01/12/2011 认为是 121 日。dayfirst=False(默认值)将把 01/12/2011 猜测为 112

# Try to infer the format for the index column
In [118]: df = pd.read_csv(
   .....:     "foo.csv",
   .....:     index_col=0,
   .....:     parse_dates=True,
   .....:     infer_datetime_format=True,
   .....: )
   .....: 

In [119]: df
Out[119]: 
            A  B  C
date               
2009-01-01  a  1  2
2009-01-02  b  3  4
2009-01-03  c  4  5
9.5 国际日期格式

虽然美国的日期格式往往是 MM/DD/YYYY,但许多国际格式使用 DD/MM/YYYY 代替。为方便起见,提供了 dayfirst 关键字

In [120]: print(open("tmp.csv").read())
date,value,cat
1/6/2000,5,a
2/6/2000,10,b
3/6/2000,15,c

In [121]: pd.read_csv("tmp.csv", parse_dates=[0])
Out[121]: 
        date  value cat
0 2000-01-06      5   a
1 2000-02-06     10   b
2 2000-03-06     15   c

In [122]: pd.read_csv("tmp.csv", dayfirst=True, parse_dates=[0])
Out[122]: 
        date  value cat
0 2000-06-01      5   a
1 2000-06-02     10   b
2 2000-06-03     15   c
9.6 将 CSV 写入二进制文件对象

df.to_csv(..., mode="wb") 允许将 CSV 写入打开的二进制模式的文件对象。

在大多数情况下,无需指定模式,因为 Pandas 会自动检测文件对象是以文本模式还是二进制模式打开的

In [123]: import io

In [124]: data = pd.DataFrame([0, 1, 2])

In [125]: buffer = io.BytesIO()

In [126]: data.to_csv(buffer, encoding="utf-8", compression="gzip")
上一篇 下一篇

猜你喜欢

热点阅读