【发布时间】:2020-09-23 17:54:08
【问题描述】:
假设我有一个 (n*m) 二进制矩阵df,类似于以下内容:
import pandas as pd
import numpy as np
df = pd.DataFrame(np.random.binomial(1, .3, size=(6,8)))
0 1 2 3 4 5 6 7
------------------------------
0 | 0 0 0 0 0 1 1 0
1 | 0 1 0 0 0 0 0 0
2 | 0 0 0 0 1 0 0 0
3 | 0 0 0 0 0 1 0 1
4 | 0 1 1 0 1 0 0 0
5 | 1 0 1 1 1 0 0 1
我想打乱矩阵中的值以创建一个形状相同的new_df,这样两个边缘分布相同,如下所示:
0 1 2 3 4 5 6 7
------------------------------
0 | 0 0 0 0 1 0 0 1
1 | 0 0 0 0 1 0 0 0
2 | 0 0 0 0 0 0 0 1
3 | 0 1 1 0 0 0 0 0
4 | 1 0 0 0 1 1 0 0
5 | 0 1 1 1 0 1 1 0
在新矩阵中,每一行的和等于原矩阵中对应行的和,同样,新矩阵中的列与原矩阵中对应列的和相同。
解决方案很容易检查:
# rows have the same marginal distribution
assert(all(df.sum(axis=1) == new_df.sum(axis=1)))
# columns have the same marginal distribution
assert(all(df.sum(axis=0) == new_df.sum(axis=0)))
如果 n*m 很小,我可以使用蛮力方法进行随机播放:
def shuffle_2d(df):
"""Shuffles a multidimensional binary array, preserving marginal distributions"""
# get a list of indices where the df is 1
rowlist = []
collist = []
for i_row, row in df.iterrows():
for i_col, val in row.iteritems():
if df.loc[i_row, i_col] == 1:
rowlist.append(i_row)
collist.append(i_col)
# create an empty df of the same shape
new_df = pd.DataFrame(index=df.index, columns=df.columns, data=0)
# shuffle until you get no repeat coordinates
# this is so you don't increment the same cell in the matrix twice
repeats = 999
while repeats > 1:
pairs = list(zip(np.random.permutation(rowlist), np.random.permutation(collist)))
repeats = pd.value_counts(pairs).max()
# populate new data frame at indicated points
for i_row, i_col in pairs:
new_df.at[i_row, i_col] += 1
return new_df
问题在于蛮力方法的扩展性很差。 (正如印第安纳琼斯和最后的十字军东征中的那句话:https://youtu.be/Ubw5N8iVDHI?t=3)
作为一个快速演示,对于 n*n 矩阵,获得可接受的随机播放所需的尝试次数如下:(一次运行)
n attempts
2 1
3 2
4 4
5 1
6 1
7 11
8 9
9 22
10 4416
11 800
12 66
13 234
14 5329
15 26501
16 27555
17 5932
18 668902
...
是否有一个简单的解决方案可以保留确切的边际分布(或告诉您没有其他可能的模式可以保留该分布)?
作为后备方案,我还可以使用一种近似算法来最小化每行的平方误差之和。
谢谢! =)
编辑: 出于某种原因,在我写这个问题之前我没有找到现有的答案,但是在发布之后它们都显示在侧边栏中:
Is it possible to shuffle a 2D matrix while preserving row AND column frequencies?
Randomize matrix in perl, keeping row and column totals the same
有时你需要做的就是问...
【问题讨论】:
-
您能否提供一个移位表应该是什么样子的示例?以及输入输入的数据结构? (它是数字数组的数组吗?)
-
当然 - 编辑了原始帖子以包含如何转换原始帖子的示例。第一行代码
df = pd.DataFrame(np.random.binomial(1, .3, size=(6,8)))将数据生成为 pandas DataFrame,尽管它也可以很容易地成为二维 numpy 数组。 -
我的尝试将基于离散选择域中的精确求解器:cp、sat 或整数编程。它们都是合理且完整的,并且可以对失败进行推理。现在,获得均匀性 并不是那么简单,方法可能会假设均匀性的重要性(以及矩阵的大小)。从琐碎的方法,例如:忽略它并希望它足够好,枚举 X 解决方案并随机选择 1 以使用更多理论通过引入约束(例如 xor)来实现更多一致性,这些约束会杀死解决方案,直到我们找到具有单个 sol
标签: python algorithm shuffle approximation