用矢量化方法替换python for循环以丢弃丢失的数据
Posted
技术标签:
【中文标题】用矢量化方法替换python for循环以丢弃丢失的数据【英文标题】:Replace python for loops with vectorized method to discard missing data 【发布时间】:2016-05-06 09:56:03 【问题描述】:我正在为数据清理操作而苦苦挣扎。我有一个包含 id、投资组合月份 (port_months) 和投资组合编号 (port) 的大型数据框,例如:
id port port_months backtest_month
49025 USA0EBZ0 0 1 1
80689 USA0EBZ0 0 2 2
224952 USA0EBZ0 0 3 4
... ... ... ...
227370 USA03BE0 1 1 12
229804 USA03BE0 1 2 13
232262 USA03BE0 1 3 14
... ... ... ...
很遗憾,我经常遇到新的id进入系统,数据不完整的情况,例如:
id port port_months backtest_month
63682 USA06W90 5 7 66
236452 USA06W90 5 8 67
238905 USA06W90 5 9 68
241358 USA06W90 5 10 69
243808 USA06W90 5 11 70
246229 USA06W90 5 12 71
这里的问题是这个id的数据进入了port_months = 7
的数据框,而不是port_months = 1
。我需要删除所有这些不完整的数据,因为另一个函数需要作用于只包含完整数据的数据集。所以,在这个例子中,我需要删除这个id,USA06W90,port = 5的数据(虽然你在这里看不到,但是port = 6的数据是完整的等等)。
我已经编写了一个简单的循环,它可以执行我想要的操作,但是速度非常慢,而且我确信我可以使用矢量化来做一些更复杂的事情:
for id in df.id:
for port in df.port.unique(): #so loop over ports where the current stock has some data, not those for which it is absent from the system
first_df = df[(df.id == id) & (df.port == port) & (df.port_months == 1)] #get the 1st row from the current port's dataframe
if first_df.empty:
df.drop(df[(df.id == id) & (df.port == port)].index, inplace = True) # drop all the rows associated with current id and port (i.e. all port_months for current port and id)
目前这需要 30 多分钟才能执行!
我一直在想一些巧妙的使用方法
groupby('id', port).apply(lambda x: x.port = x[x.port_months == 1].port)
或者什么,或者试图以某种方式使用一些技巧来构建新的投资组合并做ffill
port_new = df[df.port_months == 1].groupby('id', as_index = False).apply(lambda x: x.backtest_month / 12 )
重置索引,然后通过合并索引与df重新组合
这给出了:
id port port_months backtest_month
49025 USA0EBZ0 0 1 1
80689 USA0EBZ0 NaN 2 2
224952 USA0EBZ0 NaN 3 4
... ... ... ...
227370 USA03BE0 1 1 12
229804 USA03BE0 NaN 2 13
232262 USA03BE0 NaN 3 14
... ... ... ...
然后可以使用
填充 nansdf.fillna['port_new'](method = 'ffill')
这几乎可以工作,并且速度很快,但问题是您会遇到 id 进入然后再次离开数据集的情况,因此 ffill 也会填充所有这些 nas,而不是删除行,例如下面的 Nans 将被 5s 填充。
例如
id port port_months backtest_month
63682 USA06W90 5 11 70
236452 USA06W90 5 12 71
238905 USA06W90 NaN 1 121
241358 USA06W90 NaN 2 122
243808 USA06W90 NaN 3 123
246229 USA06W90 NaN 4 124
【问题讨论】:
【参考方案1】:要生成唯一的投资组合,您似乎需要创建一个由id
和port
组成的密钥。然后,您可以使用.loc 进行高效过滤,如下所示:
df = pd.DataFrame('backtest_month': [70, 71, 121, 122, 123],
'id': ['USA06W90', 'USA06W90', 'USA06W90', 'USA06W90', 'USA06W90'],
'port': [5, 5, 1, 1, 1],
'port_months': [11, 12, 1, 2, 3])
>>> df
id port port_months backtest_month key
63682 USA06W90 5 11 70 USA06W90_5
236452 USA06W90 5 12 71 USA06W90_5
238905 USA06W90 1 1 121 USA06W90_1
241358 USA06W90 1 2 122 USA06W90_1
243808 USA06W90 1 3 123 USA06W90_1
# Create a unique portfolio identifier.
df['key'] = df['id'] + '_' + df.port.astype(str)
# Use .loc to locate all unique portfolios that had a `port_months` value of one.
portfolios_first_month = df.loc[df.port_months == 1, 'key'].unique()
>>> portfolios_first_month
array(['USA06W90_1'], dtype=object)
# Use .loc again to locate all portfolio keys that were previously identified above.
# The colon indicates that all columns should be returned.
df_filtered = df.loc[df.key.isin(portfolios_first_month), :]
>>> df_filtered
id port port_months backtest_month key
238905 USA06W90 1 1 121 USA06W90_1
241358 USA06W90 1 2 122 USA06W90_1
243808 USA06W90 1 3 123 USA06W90_1
它生成一个包含所有唯一键的数组,其中 port_months 的值为 1(即没有丢失数据)。
df.loc[df.key.isin(portfolios_first_month), :]
然后定位所有这些键值并返回数据框中的所有列。
【讨论】:
谢谢。因此,您根据密钥必须存在于 port_months == 1 (df.key.isin(portfolios_first_month) 的基础上进行过滤。如果不是,那么与 id 及其端口相关联的特定密钥将被过滤出。聪明!大概可以用 groupby 做类似的事情? 但是我并不完全理解代码。 df.loc[df.port_months == 1, 'key'].unique()。因此,这将创建一个数组,其中每个键的 port_months == 1 的 loc。而 df.key.isin(portfolios_first_month), : 检查当前键是否有一个条目。我不熟悉这种过滤。 df['key'] = [row['id'] + '' + str(row['port']) for _, row in df.iterrows()] this行似乎会导致内存峰值(第一次杀死内核并且非常慢!需要 10 - 20 秒)。为什么不直接做 df['key'] = df['id'] + '' + str(df['port'])?那肯定会快得多。 我不确定 groupby 会在没有密钥的情况下为您提供什么。我可以获得id
和port
值,但单独使用这些值并不是很有用。我添加了一种更有效的方法来创建密钥。如果你愿意,你现在可以gb = df.groupby(portfolios_first_month)
谢谢,太好了。我不太明白你会如何使用 gb = df.groupby(portfolios_first_month) 以上是关于用矢量化方法替换python for循环以丢弃丢失的数据的主要内容,如果未能解决你的问题,请参考以下文章
当步长大于1时,通过数组切片和numpy.diff替换python中的for循环
解释numpy向量化函数应用VS python for循环的速度差异
queryList 一次抓取多个网页内容的方法--目前只有用循环 替换页码或者给出url循环进行 queryList没有像python一样的yied迭代方法 queryList 实现多个实例抓取