创建一个有限制的排列

时间:2014-11-13 11:57:22

标签: python algorithm numpy pandas permutation

我使用Python,使用Pandas和Numpy,虽然这个问题感觉像是一个更通用的算法设计问题。

我有一个列表(实际上是一个数组)的元素,我想生成该列表的排列。但是,某些项目不允许位于列表中的某些位置。我想生成一个遵守这些限制的排列。有什么方法可以做到这一点?

我实际使用的情况是Pandas数据框,有两列XYXY在不同的顺序中都具有相同的数字。数字不是唯一的。 XY中的同一行中没有数字(即没有数字与自身匹配)。我希望置换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!

2 个答案:

答案 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. 不符合标准的数据(即X == Y)
  2. 符合条件的数据(X!= Y)
  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()