【问题标题】:Find the highest value in the Matrix to maximize the score找到矩阵中的最大值以最大化分数
【发布时间】:2020-05-27 19:56:32
【问题描述】:

问题:

我想在矩阵中找到每个老师和每个组的最高值,以最大化哪个组应该与哪个老师一起去的比例。

            Teacher A   Teacher B   Teacher C   Teacher D
Group 1     50          40          20           50
Group 2     30          10          40          100
Group 3     80          60          40           20

在上表中。我知道如何找出行和列中的最高值,但我想在教师和组的组合中找到最高值,即教师不能属于两个组,而组不能属于两个教师。是的,可以有比小组更多的教师。

所以我正在寻找最终输出如下:

解决方案

Group 1 with Teacher B: 40
Group 2 with Teacher D: 100
Group 3 with Teacher A: 80

我目前的工作 我已经尝试了几种方法来使用 pandas 来解决这个问题,但所有内容都只获取行和列的最高值,或者充其量是最高键的名称。我按照教程here 但没有取得太大的成功。任何指导都会很棒。

【问题讨论】:

    标签: python python-3.x pandas matrix


    【解决方案1】:

    首先搜索所有可能的排列,然后取最大值作为值的总和,最后打印出来。这是我的数据框实现:

    import itertools
    m = [
        [50, 40, 20, 50],
        [30, 10, 40, 100],
        [80, 60, 40, 20]
    ]
    rows = ['Group 1', 'Group 2', 'Group 3']
    cols = ['Teacher A', 'Teacher B', 'Teacher C', 'Teacher D']
    df = pd.DataFrame(m, index=rows, columns=cols)
    
    permuts = itertools.permutations(cols, len(rows))
    
    L = []
    for p in permuts:
        s = 0
        d = {}
        for i, r in enumerate(rows):
            s += df[p[i]][r]
            d[r] = p[i]
        obj = [s, d]
        L.append(obj)
    
    result = max(L, key=lambda x: x[0])
    # [220, {'Group 1': 'Teacher B', 'Group 2': 'Teacher D', 'Group 3': 'Teacher A'}]
    # Here 220 is the maximum sum you can have
    
    result_dict = result[1]
    # {'Group 1': 'Teacher B', 'Group 2': 'Teacher D', 'Group 3': 'Teacher A'}
    
    for i, v in result_dict.items():
        print("{} with {} : {}".format(i, v, df[v][i]))
    
    # Group 1 with Teacher B : 40
    # Group 2 with Teacher D : 100
    # Group 3 with Teacher A : 80
    

    说明

    这是一个关于itertools.permutations 工作原理的小例子。数字2是每个排列的长度,['a','b','c']是排列的元素:

    import itertools
    permuts = itertools.permutations(['a','b','c'],2)
    for i in a:
        print(i)
    

    输出:(这里有 6 个排列)

    ('a', 'b')
    ('a', 'c')
    ('b', 'a')
    ('b', 'c')
    ('c', 'a')
    ('c', 'b')
    

    在我们的例子中,我们有 3 个组,因此我们需要 4 个可用教师中的 3 个(教师 A、B、C 和 D)。例如排列('Teacher A', 'Teacher B', 'Teacher C') 表示Group1=Teacher A, Group2=Teacher B, Group3=Teacher C)

    所以我们将用permuts = itertools.permutations(cols, len(rows))枚举3位老师的所有有序排列:

    ('Teacher A', 'Teacher B', 'Teacher C')
    ('Teacher A', 'Teacher B', 'Teacher D')
    ('Teacher A', 'Teacher C', 'Teacher B')
    ...
    ('Teacher D', 'Teacher C', 'Teacher A')
    ('Teacher D', 'Teacher C', 'Teacher B')
    

    所以在我们的变量permuts中得到24个元组

    然后我们计算每个排列的值的总和,我们得到一个包含这些元素的大列表:

    L = []
    for p in permuts:
        s = 0
        d = {}
        for i, r in enumerate(rows):
            s += df[p[i]][r]
            d[r] = p[i]
        obj = [s, d]
        L.append(obj)
    

    输出 L:

    [
        [100, {'Group 1': 'Teacher A', 'Group 2': 'Teacher B', 'Group 3': 'Teacher C'}]
        [80, {'Group 1': 'Teacher A', 'Group 2': 'Teacher B', 'Group 3': 'Teacher D'}]
    ...
        [220, {'Group 1': 'Teacher B', 'Group 2': 'Teacher D', 'Group 3': 'Teacher A'}]
    ]
    ...
    

    第一个数字(例如 100、80 和 220)表示此特定排列的值的总和。

    然后我们选择和最大的排列,这里是220

    result = max(L, key=lambda x: x[0])
    # [220, {'Group 1': 'Teacher B', 'Group 2': 'Teacher D', 'Group 3': 'Teacher A'}]
    

    最后,我们用print("{} with {} : {}".format(i, v, df[v][i])) 的数据帧中的值打印排列。 例如df["Teacher B"]["Group 1"] = 40

    Group 1 with Teacher B : 40
    Group 2 with Teacher D : 100
    Group 3 with Teacher A : 80
    

    【讨论】:

    • 哇,这正是我想要的。感谢您提供解决方案。请问您是否可以解释一下您的方法。我以前从未使用过 itertools.permutations。所以对代码有一些了解会很棒!
    • 我为您添加了解释,如果您还有问题,请不要犹豫!
    【解决方案2】:

    这看起来像是一个优化问题。

    你有两种方法来处理它(从理论上讲)。

    1. 启发式:

      除了病态用例,我们可以认为矩阵中的最大值将在最终结果中结束。这里我们有 100 组 2 和教师 D。然后我们删除组 2 的行和教师 D 的列并迭代。

      这一步一步给出:

      Group 2    Teacher D   100
      Group 3    Teacher A    80
      Group 1    Teacher B    50
      
    2. 详尽

      前面的方法会导致正确的结果是值有很大的差异,但如果值彼此太接近,则只能找到接近最大值的解。穷举方法包括计算每个可能组合的值的总和并保持最高值。它当然会给出相同的结果,但是我需要太多的操作才能在这里手动显示它......

    Python 翻译

    第一种方法是迭代但简单:

    # heuristic
    
    dfA = df
    result = {}
    
    while (len(dfA) > 0):
        mx = dfA.max()     # find max per teacher
        mmx = pd.Series(mx[mx == mx.max()])  # find absolute max of matrix
        teacher = mmx.index[0]                       # get teacher
        val = mmx.values[0]                          # get value
        group = dfA[dfA[teacher] == val].index[0]    # get group
        result[group] = (teacher, val)               # store the triplet
        dfA = dfA.drop(index = group).drop(columns = teacher) # remove the row and column
    
    dfout = pd.DataFrame(result).T
    print(dfout.to_string())
    

    按预期提供:

                     0    1
    Group 2  Teacher D  100
    Group 3  Teacher A   80
    Group 1  Teacher B   40
    

    第二种方法更具确定性,但可能无法扩展到大型数据集:

    import itertools
    
    # compute with itertools all the possible permutations of group-teachers
    mindex = pd.MultiIndex.from_tuples(itertools.permutations(df.columns, len(df)))
    
    # compute the total value for each permutation
    total = pd.DataFrame(data = 0, columns=mindex, index=df.index
                         ).transform(lambda x: pd.Series(
                             [df.loc[x.index[i], x.name[i]]
                              for i in range(len(x))], index=x.index)).sum()
    
    # prepare the resulting dataframe
    dfout = pd.DataFrame({'Groups': df.index,
                          'Teachers': total[total == total.max()].index[0]})
    
    # extract the value per group
    dfout['val'] = dfout.apply(lambda x: df.loc[x['Groups'], x['Teachers']], axis=1)
    
    print(dfout.to_string())
    

    它给出与预期相同的值

        Groups   Teachers  val
    0  Group 1  Teacher B   40
    1  Group 2  Teacher D  100
    2  Group 3  Teacher A   80
    

    【讨论】:

    • 感谢您的精彩解释。是的,我注意到,当我使用排列时,听起来更像是蛮力方法,对于较小的矩阵,它可以工作,但对于 30X30 的矩阵,它需要大量的时间和资源。
    • 这是一个不适用于启发式方法的案例:m = [ [50, 40, 20, 50], [30, 10, 90, 100], [10, 60, 40, 80] ] 好的解决方案是[90, 80, 50],但如果你先取最高值,你会得到[100, 60, 50]
    • @phoenixo 确实,我经历过,我理解为什么会这样,但使用排列的问题是,对于更大的矩阵,我永远不会得到输出,而且我的 CPU 和内存使用率正在下降高达 100%。
    • 是的,当然,这取决于您的矩阵在您的用例中是 5X5 还是 30X30
    【解决方案3】:

    计算行和列的最佳组合以优化转化。我使用了使用匈牙利算法的 linear_sum_assignment 包。更多可以了解here

    from scipy.optimize import linear_sum_assignment
    import pandas as pd
    
    df = pd.read_csv("myfile.csv", index_col=0)
    gain = df.to_numpy()
    row_ind, col_ind = linear_sum_assignment(gain, maximize=True)
    print(row_ind)
    print(col_ind)
    print(gain[row_ind, col_ind].sum())
    

    【讨论】:

    • 当我尝试这个时,我得到一个错误 - TypeError: linear_sum_assignment() got an unexpected keyword argument 'maximize'
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-06-17
    • 2015-05-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多