Python 更快、更友好地完成此算法?
我有一个非常大的数据框,其中每个元素都填充了1-5个整数,如果没有该元素的数据,则填充为0。我想创建它的两个调整副本:Python 更快、更友好地完成此算法?,python,pandas,numpy,Python,Pandas,Numpy,我有一个非常大的数据框,其中每个元素都填充了1-5个整数,如果没有该元素的数据,则填充为0。我想创建它的两个调整副本: train将是一个副本,其中每行随机20%的非零元素设置为0 test将是一个副本,其中除这些相同的20%元素外,所有元素都设置为0 以下是一个示例: ORIGINAL 0 1 2 3 4 5 6 7 8 9 0 3 0 1 1 3 5 3 5 4 2 1 4 2 3 2 3 3 4 4 1 2 2 2 4
将是一个副本,其中每行随机20%的非零元素设置为0train
将是一个副本,其中除这些相同的20%元素外,所有元素都设置为0test
ORIGINAL
0 1 2 3 4 5 6 7 8 9
0 3 0 1 1 3 5 3 5 4 2
1 4 2 3 2 3 3 4 4 1 2
2 2 4 2 5 4 4 0 0 4 2
TRAIN
0 1 2 3 4 5 6 7 8 9
0 3 0 0 1 3 5 3 5 4 2
1 4 2 3 0 3 3 4 4 0 2
2 2 4 2 5 4 4 0 0 4 0
TEST
0 1 2 3 4 5 6 7 8 9
0 0 0 1 0 0 0 0 0 0 0
1 0 0 0 2 0 0 0 0 1 0
2 0 0 0 0 0 0 0 0 0 2
下面是我当前的暴力算法,它完成了任务,但速度太慢了:
train, test = original.copy(), original.copy()
for i in range(original.shape[0]):
print("{} / {}".format(i + 1, original.shape[0]))
row = original.iloc[i] # Select row
nonZeroIndices = np.where(row > 0)[0] # Find all non-zero indices
numTest = int(len(nonZeroIndices) * 0.2) # Calculate 20% of this amount
rand = np.random.choice(nonZeroIndices, numTest, replace=False) # Select a rancom 20% of non-zero indices
for j in range(original.shape[1]):
if j in rand:
train.iloc[i, j] = 0
else:
test.iloc[i, j] = 0
使用Pandas或Numpy有没有更快的方法来实现这一点?一种方法是
def make_train_test(df):
train, test = df.copy(), df.copy()
for i, row in df.iterrows():
non_zero = np.where(row > 0)[0]
num_test = int(len(non_zero) * 0.2)
rand = np.random.choice(non_zero, num_test, replace=False)
row_train = train.iloc[i, :]
row_test = test.iloc[i, :]
row_train[rand] = 0
row_test[~row_test.index.isin(rand)] = 0
return train, test
在我的测试中,这大约需要4.85毫秒,您的原始解决方案大约需要9.07毫秒,andrew_reece(其他优雅的)解决方案需要15.6毫秒。首先,使用
sample()
创建非零值的20%子集:
subset = df.apply(lambda x: x[x.ne(0)].sample(frac=.2, random_state=42), axis=1)
subset
1 2 5 8
0 NaN 1.0 NaN 4.0
1 2.0 NaN NaN 1.0
2 4.0 NaN 4.0 NaN
现在可以通过将子集
与原始df
相乘,并使用1s或0s作为填充值来设置列车
和测试
:
train = df.apply(lambda x: x.multiply(subset.iloc[x.name].isnull(), fill_value=1), axis=1)
train
0 1 2 3 4 5 6 7 8 9
0 3 0 0 1 3 5 3 5 0 2
1 4 0 3 2 3 3 4 4 0 2
2 2 0 2 5 4 0 0 0 4 2
test = df.apply(lambda x: x.multiply(subset.iloc[x.name].notnull(), fill_value=0), axis=1)
test
0 1 2 3 4 5 6 7 8 9
0 0 0 1 0 0 0 0 0 4 0
1 0 2 0 0 0 0 0 0 1 0
2 0 4 0 0 0 4 0 0 0 0
数据:
非常优雅,谢谢。最后,df.sample
中的random_state
是否只是为了再现性?我真的不需要它吗?不客气。这是正确的,在这里设置random_state
只是为了再现性,您不需要它。这实际上似乎比给定测试数据集上的原始解决方案慢得多(参见下面答案中的计时)。与我在大数据帧上的解决方案相比,它实际上快得惊人(秒vs分钟)的确,熊猫的奔跑速度通常比它所覆盖的一些Numpy方法要慢。而使用apply
基本上是一个美化的for循环,没有巨大的矢量化收益。我想可能有一种很好的方法可以一次绘制整个子集
,而不是像这里的apply()
那样对每一行调用sample()
。欢迎编辑!我理解你的方法可能更快,但我的目标是提高我与熊猫的技能,而这个问题特别要求更友好的解决方案,因此我保留安德鲁·里斯的答案作为公认的答案。不过,还是投票支持效率。很好的效率@fuglede!
df
0 1 2 3 4 5 6 7 8 9
0 3 0 1 1 3 5 3 5 4 2
1 4 2 3 2 3 3 4 4 1 2
2 2 4 2 5 4 4 0 0 4 2