Python 创建具有限制的排列

Python 创建具有限制的排列,python,algorithm,numpy,pandas,permutation,Python,Algorithm,Numpy,Pandas,Permutation,我使用的是Python,还有Pandas和Numpy,尽管这个问题感觉像是一个更通用的算法设计问题 我有一个元素列表(实际上是一个数组),我想生成该列表的排列。但是,某些项目不允许位于列表中的某些位置。我想生成一个服从这些限制的排列。做这件事的有效方法是什么 我的实际用法是case是一个Pandas数据帧,有两列X和YX和Y都有相同的数字,顺序不同。数字不是唯一的。同一行中的X和Y中没有出现任何数字(即,没有数字与自身匹配)。我想排列Y,同时保留没有数字与其自身匹配的限制。我一直在Y上调用Num

我使用的是Python,还有Pandas和Numpy,尽管这个问题感觉像是一个更通用的算法设计问题

我有一个元素列表(实际上是一个数组),我想生成该列表的排列。但是,某些项目不允许位于列表中的某些位置。我想生成一个服从这些限制的排列。做这件事的有效方法是什么

我的实际用法是case是一个Pandas数据帧,有两列
X
Y
X
Y
都有相同的数字,顺序不同。数字不是唯一的。同一行中的
X
Y
中没有出现任何数字(即,没有数字与自身匹配)。我想排列
Y
,同时保留没有数字与其自身匹配的限制。我一直在
Y
上调用Numpy的
permute
,但是大约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
在我们的例子中,这将是:

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
问题是这太慢了,而且根本没有并行化。有没有办法将其并行化?我想答案是“不”,因为在一行进行的互换会影响下一行有效的“其他”互换

编辑比例感:
大约1.4*10^7行,X中有2*10^6个唯一值,Y中有一个类似的数字。需要生成大约10^3个独立排列。实际上,我将行分组并独立地排列它们的内容,有些组非常小(例如10行),但许多组相当大(10^5)。这会带来一点帮助,但最终会有很多争吵!在10^7行上运行一个简单的
np.random.permutation
大约需要7秒钟,这就足够了。运行上面的受限排列算法(使用numpy而不是熊猫来实现,以提高速度)只需7秒钟就可以运行10^3行。Eep

你为什么不做你正在做的事情(排列Y),但在最后检查以确保没有匹配:

if (df.X == df.Y).any():
    reject_dataframe()

我希望我没有提出一个对你的例子太具体的解决方案。但是,如果可行,可以创建每个排列,然后删除不符合条件的排列。然后,您可以直接使用该样本,也可以从结果排列中随机抽取样本

下面是受上述示例启发而编写的代码。我意识到我使用的是稍微不同的开始假设:

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
编辑: 我将把上面所有的组合垃圾放在那里,因为其他人可能会发现它很有用。但是在评论中聊天之后,我想我有了一个可能的解决办法

看起来您可以进行置换,然后将置换后的数据帧分为两个子集:

  • 不符合标准的数据(即X==Y)
  • 符合条件的数据(X!=Y)
  • 然后我们可以取第一个子集,然后简单地重新排列它。子集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)})
    
    示例数据将以一些重复的行开始,但这没关系。让我们创建洗牌函数:

    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。那可能行得通

    你能举个例子吗?在使用
    置换
    函数后过滤掉这些值就足够了吗?如何过滤?可能是排列过程匹配,例如所有2相互匹配。然后仅获取X==Y的值并对其进行排列不会解决任何问题;结果的排列是与之前相同的2集。你说“X和Y都有相同的数字,顺序不同”,但在你的例子中Y不包含1,X不包含3,所以我不确定你的意思。我认为你的问题是:X和Y是任意的多集,你想得到一对,这样一边的所有值组成X,另一边的值组成Y,没有值与自身配对。这是对的吗?我要说清楚的是,你想要任何这样的配对,你并没有试图公平地选择一个或任何东西。如果不太可能出现不好的行,那么这可能是可行的。然而,没有坏的行是不太可能的。在我的用例中,我说“大约1%的结果行的X==Y”。因此,只要重新掷骰子直到得到有效结果,就需要花费上亿次。哦,我完全误解了这一点。我以为你说的是1%的数据帧,不是行。是的,这需要很长时间,很抱歉。这是一个有趣的开始!如果由于内存限制,无法枚举每个排列,我们将如何修改此选项?我想得很清楚:1)排列子集2)构建一个循环来排列并检查条件,然后将结果写入文件。这样,一次只有一个条件在内存中。如果以后需要随机结果,可以打开文件,计算行数,随机选择一行。我想这取决于用例。我刚刚阅读了关于你问题的附加评论,我怀疑你得到的答案没有帮助,因为回答者在探索你问题的规模方面遇到了困难。你能以某种方式分享这个问题的数量级吗?真正的问题是100个变量,每个变量有30个值吗?1000 x 300?100 x 3e10?我想答案真的取决于你的规模。谢谢!是的,我的答案“全部计算”不适合你的问题。做一个循环太慢了。因此,我们需要将其表述为一个numpy问题,以获得