如何消除数据框中的嵌套循环
How to eliminate nested loops in a dataframe
我有一个包含许多列的 df,这些列代表构成指数的公司的市值。 df 的索引是 dates.
每 63 days/rows 我想消除,对于接下来的 63 days/rows 除了 500 个最大的值之外的所有值。
换句话说:对于前 63 个 days/rows,应该显示的唯一值是来自第一行市值最大的 500 家公司的值。
例如:
[in]: pd.DataFrame(np.array([[1, 1, 0.5], [5 ,2, 10], [1,3, 10],[4,2, 10]]),
columns=['a', 'b','c'])
[out]: a b c
0 1.0 1.0 0.5
1 5.0 2.0 10.0
2 1.0 3.0 10.0
3 4.0 2.0 10.0
假设在这个例子中我想使用 2 days/rows。所需的输出将是:
a b c
0 1.0 1.0 NaN
1 5.0 2.0 NaN
2 NaN 3.0 10.0
3 NaN 2.0 10.0
这是我现在使用的代码。它有效,但需要很长时间。
for x in range(0,len(dfcap)/63 - 1):
lst = list()
for value in dfcap.iloc[x*63].nlargest(500):
lst.append((dfcap == value).idxmax(axis=1)[x*63])
for column in dfcap.columns:
for n in range(x*63,x*63 + 63):
if column not in lst: dfcap[column][n] = 0
如果我理解你的问题,这对你来说应该会快得多。
这是我在 Intel i5 上的 VM 运行 中 630k 行 x 1000 列的 %%timeit
输出。
%%timeit -n 2 -r 2
19.3 s ± 549 ms per loop (mean ± std. dev. of 2 runs, 2 loops each)
import pandas as pd
import numpy as np
import random, string
def randomticker(length):
""" Generate random uppercase string of length given """
letters = string.ascii_uppercase
return ''.join(random.choice(letters) for i in range(length))
# generate random data, 630k rows (dates) by 1000 columns (companies)
data = np.random.rand(63 * 10000,1000)
# generate 1000 random uppercase strings (stock tickers)
companies = [randomticker(4) for x in range(1000)]
df = pd.DataFrame(data, columns=companies)
# Number of columns to make NA, in your case (width of DF - 500)
mask_na_count = len(df.columns) - 500
# If your index is not sorted 0-n integers use this line
# df = df.reset_index(drop=True)
for x in range(0,len(df)//63 - 1):
# Get the smallest (width-500) valued column names at x*63 index
na_cols = df.iloc[x*63].nsmallest(mask_na_count).index
# Grab chunk of 63 rows and make smallest columns np.nan
df.loc[(x-1)*63:x*63, na_cols] = np.nan
如果您再次需要索引作为日期,您可以在重置前保存索引,然后再次应用索引
save_index = df.index
和 df.index = save_index
我有一个包含许多列的 df,这些列代表构成指数的公司的市值。 df 的索引是 dates.
每 63 days/rows 我想消除,对于接下来的 63 days/rows 除了 500 个最大的值之外的所有值。
换句话说:对于前 63 个 days/rows,应该显示的唯一值是来自第一行市值最大的 500 家公司的值。
例如:
[in]: pd.DataFrame(np.array([[1, 1, 0.5], [5 ,2, 10], [1,3, 10],[4,2, 10]]),
columns=['a', 'b','c'])
[out]: a b c
0 1.0 1.0 0.5
1 5.0 2.0 10.0
2 1.0 3.0 10.0
3 4.0 2.0 10.0
假设在这个例子中我想使用 2 days/rows。所需的输出将是:
a b c
0 1.0 1.0 NaN
1 5.0 2.0 NaN
2 NaN 3.0 10.0
3 NaN 2.0 10.0
这是我现在使用的代码。它有效,但需要很长时间。
for x in range(0,len(dfcap)/63 - 1):
lst = list()
for value in dfcap.iloc[x*63].nlargest(500):
lst.append((dfcap == value).idxmax(axis=1)[x*63])
for column in dfcap.columns:
for n in range(x*63,x*63 + 63):
if column not in lst: dfcap[column][n] = 0
如果我理解你的问题,这对你来说应该会快得多。
这是我在 Intel i5 上的 VM 运行 中 630k 行 x 1000 列的 %%timeit
输出。
%%timeit -n 2 -r 2
19.3 s ± 549 ms per loop (mean ± std. dev. of 2 runs, 2 loops each)
import pandas as pd
import numpy as np
import random, string
def randomticker(length):
""" Generate random uppercase string of length given """
letters = string.ascii_uppercase
return ''.join(random.choice(letters) for i in range(length))
# generate random data, 630k rows (dates) by 1000 columns (companies)
data = np.random.rand(63 * 10000,1000)
# generate 1000 random uppercase strings (stock tickers)
companies = [randomticker(4) for x in range(1000)]
df = pd.DataFrame(data, columns=companies)
# Number of columns to make NA, in your case (width of DF - 500)
mask_na_count = len(df.columns) - 500
# If your index is not sorted 0-n integers use this line
# df = df.reset_index(drop=True)
for x in range(0,len(df)//63 - 1):
# Get the smallest (width-500) valued column names at x*63 index
na_cols = df.iloc[x*63].nsmallest(mask_na_count).index
# Grab chunk of 63 rows and make smallest columns np.nan
df.loc[(x-1)*63:x*63, na_cols] = np.nan
如果您再次需要索引作为日期,您可以在重置前保存索引,然后再次应用索引
save_index = df.index
和 df.index = save_index