Pandas 自定义函数来查找是否是第一、第二等星期一、星期二等 - 欢迎所有建议

2024-01-08

所以我有以下代码,它读取 5 列,日期 ohlc。然后,它创建一个列“dow”来保存星期几。到目前为止,一切都很好:

import numpy as np
import pandas as pd
df = pd.read_csv('/content/drive/MyDrive/Forex/EURUSD-2018_12_18-2020_11_01.csv',parse_dates=True,names = ['date','1','2','3','4',])

df['date'] = pd.to_datetime(df['date'])
df.index = df['date']
df['dow'] = df['date'].dt.dayofweek
#df['downum'] = df.apply(lambda x: downu(x['date']))
df

产生以下输出:

                    date                1       2       3       4       dow
date                        
2018-12-18 00:00:00 2018-12-18 00:00:00 1.13498 1.13497 1.13508 1.13494 1
2018-12-18 00:01:00 2018-12-18 00:01:00 1.13497 1.13500 1.13500 1.13496 1
2018-12-18 00:02:00 2018-12-18 00:02:00 1.13500 1.13498 1.13502 1.13495 1
2018-12-18 00:03:00 2018-12-18 00:03:00 1.13498 1.13513 1.13513 1.13498 1
2018-12-18 00:04:00 2018-12-18 00:04:00 1.13513 1.13511 1.13515 1.13511 1
... ... ... ... ... ... ...
2020-11-01 23:55:00 2020-11-01 23:55:00 1.16402 1.16408 1.16410 1.16401 6
2020-11-01 23:56:00 2020-11-01 23:56:00 1.16409 1.16408 1.16410 1.16405 6
2020-11-01 23:57:00 2020-11-01 23:57:00 1.16409 1.16417 1.16418 1.16408 6
2020-11-01 23:58:00 2020-11-01 23:58:00 1.16417 1.16416 1.16418 1.16414 6
2020-11-01 23:59:00 2020-11-01 23:59:00 1.16418 1.16419 1.16419 1.16413 6

现在我想添加以下自定义函数:

def downu(dtime):
  d = dtime.dt.day
  x = np.ceil(d/7)
  return x

并在显示数据框之前调用它,如下所示:

df['downum'] = df.apply(lambda x: downu(x['date']))

添加一列,指示该月的第一个“1”、第二个“2”...第五个“5”xxxday

但这会产生以下错误:

---------------------------------------------------------------------------
ParserError                               Traceback (most recent call last)
pandas/_libs/tslibs/conversion.pyx in pandas._libs.tslibs.conversion._convert_str_to_tsobject()

pandas/_libs/tslibs/parsing.pyx in pandas._libs.tslibs.parsing.parse_datetime_string()

/usr/local/lib/python3.6/dist-packages/dateutil/parser/_parser.py in parse(timestr, parserinfo, **kwargs)
   1373     else:
-> 1374         return DEFAULTPARSER.parse(timestr, **kwargs)
   1375 

11 frames
ParserError: Unknown string format: date

During handling of the above exception, another exception occurred:

ValueError                                Traceback (most recent call last)
pandas/_libs/tslibs/timestamps.pyx in pandas._libs.tslibs.timestamps.Timestamp.__new__()

pandas/_libs/tslibs/conversion.pyx in pandas._libs.tslibs.conversion.convert_to_tsobject()

pandas/_libs/tslibs/conversion.pyx in pandas._libs.tslibs.conversion._convert_str_to_tsobject()

ValueError: could not convert string to Timestamp

The above exception was the direct cause of the following exception:

KeyError                                  Traceback (most recent call last)
/usr/local/lib/python3.6/dist-packages/pandas/core/indexes/datetimes.py in get_loc(self, key, method, tolerance)
    603                 key = self._maybe_cast_for_get_loc(key)
    604             except ValueError as err:
--> 605                 raise KeyError(key) from err
    606 
    607         elif isinstance(key, timedelta):

KeyError: 'date'

我在类似情况下看到过以下建议:

df['downum'] = df.apply(lambda x: downu(x.date))

但这会产生以下(可以理解的)错误:

---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-33-7f9aa69c7ea7> in <module>()
     12 df.index = df['date']
     13 df['dow'] = df['date'].dt.dayofweek
---> 14 df['downum'] = df.apply(lambda x: downu(x.date))
     15 df

5 frames
/usr/local/lib/python3.6/dist-packages/pandas/core/generic.py in __getattr__(self, name)
   5139             if self._info_axis._can_hold_identifiers_and_holds_name(name):
   5140                 return self[name]
-> 5141             return object.__getattribute__(self, name)
   5142 
   5143     def __setattr__(self, name: str, value) -> None:

AttributeError: 'Series' object has no attribute 'date'

有什么解决办法吗?


Try:

df['downum'] = df['date'].apply(downu)

并将 downu 更改为:

def downu(dtime):
  d = dtime.day      # use dtime.day rather than dtime.dt.day
  x = np.ceil(d/7)
  return int(x)      # cast to int since d/7 is float even after np.ceil()

df.apply() 适用于整个 df,即依次(按列)所有列。每个处理列Series的索引仍然是DataFrame索引。因此,列标签“日期”不能用作正在处理的中间系列的索引。您必须在“日期”系列上使用 apply() ,除非您的 downu() 函数可以接受所有列的值并忽略不相关的列。

Edit:

使用添加替代解决方案apply(... ,axis=1) and list(map(...))

这里有进一步的解决方案,其中一些我认为是OP最初尝试的目标编码方式。我还将讨论它们在大型数据集的系统性能(执行时间)以及程序可读性(清晰度)方面的优缺点。

替代解决方案 1:

%%timeit
df['downum'] = df.apply(lambda x: downu(x['date']), axis=1)

988 µs ± 8.26 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

替代解决方案 2:

%%timeit
df['downum'] = df.apply(lambda x: downu(x.date), axis=1)

1.01 ms ± 13.4 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

替代解决方案 3:

%%timeit
df['downum'] = list(map(downu, df['date']))

244 µs ± 3.4 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

原解决方案:

%%timeit 
df['downum'] = df['date'].apply(downu)

810 µs ± 484 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

替代解决方案 1 和 2 应该是 OP 最初尝试的目标编码格式。唯一的区别是axis=1添加以使它们可行。现在,与axis=1添加到DataFrame.apply()功能,downu(x['date'] and downu(x.date)可以在 lambda 函数中使用。有效地,axis=1改变行为DataFrame.apply()函数允许使用列索引名称。通过认为 apply() 内部的函数是通过 Series 对象按行传递,可以更好地理解这一点。 Series 对象具有原始 DataFrame 列名称/索引,现在成为 Series 索引。因此,您可以按照与访问 Series 元素相同的方式来访问这些元素,方法是按照 series_obj['index'] 等格式进行编码。

解决方案比较:

比较原始解决方案的执行时间(使用pandas.Series.apply())与 2 个替代解决方案使用pandas.DataFrame.apply(... ,axis=1),原来的解决方案还是快了一点。在程序可读性方面,原始解决方案的工作原理是df['date']pandas 系列被认为简单且更好。

考虑到系统性能,替代方案3使用list(map(...)) is 速度提高 3 倍 ~ 4 倍比所有其他解决方案。请注意,此性能比较结果为DataFrame.apply(..., axis=1) vs list(map(..))是通用的而不是特定于这个问题的。你可以参考这个answer https://stackoverflow.com/a/46923192/15070697帖子的如何将函数应用于 Pandas 数据框的两列 https://stackoverflow.com/q/13331698/15070697以便对该主题进行更深入的讨论。同一篇文章的一些其他答案对于更好地理解 apply() 函数也非常有用。

综上所述,如果数据集不大且系统性能不是主要考虑因素,请使用原始解决方案pandas.Series.apply()有利于程序的可读性和清晰度。否则,出于系统性能考虑,使用list(map(...))远远优于pandas.DataFrame.apply(... ,axis=1)方法。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Pandas 自定义函数来查找是否是第一、第二等星期一、星期二等 - 欢迎所有建议 的相关文章

随机推荐