我使用Python,使用Pandas和Numpy,虽然这个问题感觉像是一个更通用的算法设计问题。
我有一个列表(实际上是一个数组)的元素,我想生成该列表的排列。但是,某些项目不允许位于列表中的某些位置。我想生成一个遵守这些限制的排列。有什么方法可以做到这一点?
我实际使用的情况是Pandas数据框,有两列X
和Y
。 X
和Y
在不同的顺序中都具有相同的数字。数字不是唯一的。 X
和Y
中的同一行中没有数字(即没有数字与自身匹配)。我希望置换Y
,同时保持没有数字与自身匹配的限制。我在permute
上一直在调用Numpy的Y
,但大约1%的结果行有X==Y
。
使用示例编辑:
import pandas as pd
import numpy as np
data = [[1,2],
[1,4],
[4,2],
[2,3]]
df = pd.DataFrame(columns=['X', 'Y'],
data=data)
df_permuted = df.copy()
df_permuted.Y = np.random.permutation(df.Y)
print(df.X==df.Y)
#0 False
#1 False
#2 False
#3 False
#dtype: bool
print(df_permuted.X==df_permuted.Y)
#0 False
#1 False
#2 False
#3 True
#dtype: bool
编辑: 明显的算法太慢/无法扩展,这是:
for every row i:
define the set of valid candidate "other" rows (where i.X != other.Y and i.Y != other.X)
grab a row from the valid set
swap i.Y and other.Y
在我们的Pandas示例中,这将是:
from numpy.random import choice
for i in df.index:
other_rows = df[(df.ix[i].X != df.Y) * (df.ix[i].Y != df.X)]
selected_row = choice(other_rows.index)
original_Y = df.ix[i].Y
df.ix[i].Y = df.ix[selected_row].Y
df.ix[selected_row].Y = original_Y
print(df.X==df.Y)
#0 False
#1 False
#2 False
#3 False
#dtype: bool
问题是这个太慢了,根本没有并行化。有没有办法并行化它?我想答案是" No",因为在一行上进行的掉期影响了有效的"其他"为下一行。
编辑规模感:
大约1.4 * 10 ^ 7行,X *中的2 * 10 ^ 6个唯一值和Y中的相似数。并且需要生成大约10 ^ 3个独立的排列。我实际上采取行组并独立地置换它们的内容,并且一些组非常小(例如10行),但是许多组相当大(10 ^ 5)。这有点帮助,但最后有很多行!只需在10 ^ 7行上运行一个简单的np.random.permutation
大约需要7秒,这就足够了。运行上面的受限排列算法(在numpy而不是pandas中实现以增加速度)仅需10秒,每行10秒。 EEP!
答案 0 :(得分:1)
我希望我没有提出一个对你的例子过于具体的解决方案。但是,如果可行,您可以创建每个排列,然后删除那些不符合您条件的排列。然后你可以直接使用它,也可以从结果排列中随机抽样。
此处的代码受上述示例的启发。我意识到我使用了稍微不同的开始假设:
df = pd.DataFrame( list(itertools.product([1,2,3,4], [1,2,3,4])), columns = ['X','Y'])
print df
X Y
0 1 1
1 1 2
2 1 3
3 1 4
4 2 1
5 2 2
6 2 3
7 2 4
8 3 1
9 3 2
10 3 3
11 3 4
12 4 1
13 4 2
14 4 3
15 4 4
然后设置您感兴趣的标准:
print df[df.X != df.Y]
X Y
1 1 2
2 1 3
3 1 4
4 2 1
6 2 3
7 2 4
8 3 1
9 3 2
11 3 4
12 4 1
13 4 2
14 4 3
<强> 修改 强>: 我将所有上述组合垃圾留在那里,因为其他人可能会觉得它很有用。但在评论中聊天后,我想我有一个可能的解决方案。
看起来你可以进行排列,然后将置换的数据帧分成两个子集:
然后我们可以采用第一个子集,然后再次进行置换。子集1应该比子集2小得多。我们只是递归地执行此操作,创建一组符合条件的记录应该非常简单快捷。
当然,我们必须处理只有一行匹配的情况。
我已经实施了一个示例解决方案:
设置一些与真实数据大小相似的播放数据:
np.random.seed(3)
n=14000000
df = pd.DataFrame({'X' : np.random.randint(2000000, size=n),
'Y' : np.random.randint(2000000, size=n)})
示例数据将从一些重复的行开始,但是没关系。让我们创建一个shuffle函数:
def permuteDataFrame(inDf):
permutedDf = pd.DataFrame({'X' : np.random.permutation(inDf.X),
'Y' : np.random.permutation(inDf.Y)})
# check for dupes
clash = permutedDf[permutedDf.X == permutedDf.Y]
if clash.shape[0] > 1: #repermuting can't work if only one row has a match
clash = permutedDf[permutedDf.X == permutedDf.Y].copy()
noclash = permutedDf[permutedDf.X != permutedDf.Y].copy()
# recursion FTW: run the clashes back through this algo
clash = permuteDataFrame(clash)
permutedDf = pd.concat([clash, noclash ])
if clash.shape[0] == 1: # handle the single match problem
# solving the single match by grabbing the single match plus a random other record and permuting
# get the vector of bools that indicate matches
clashIndex = permutedDf.X == permutedDf.Y
# randomly make another one True
ilocToSwap = np.random.randint(permutedDf.shape[0]) # random record location to swap
indexOfClashes.iloc[ilocToSwap] = True
clash = permutedDf[indexOfClashes]
# recursion FTW: run the clashes back through this algo
clash = permuteDataFrame(clash)
permutedDf = pd.concat([clash, noclash ])
return permutedDf
在我的Mac上,一个简单的排列需要5.3秒。新permuteDataFrame()
函数需要5.8秒。即使您的机器需要8秒钟,在2.2小时内也能获得1000秒。那可能有用。
答案 1 :(得分:0)
为什么你不做你正在做的事情(置换Y),但最后只是检查以确保没有匹配:
if (df.X == df.Y).any():
reject_dataframe()