Python 寻找有序多类情形的分界点

Python 寻找有序多类情形的分界点,python,algorithm,numpy,optimization,scipy,Python,Algorithm,Numpy,Optimization,Scipy,现在我有了一个表数据,它有50000行(例如表示不同的用户)和两列:一个用于类“a”到“E”的标签,另一个用于小数点为0.0到14000000.00的分数。 我的任务是根据第二列的分数,将一个新标签附加到“a”到“E”的用户上 在这里,我希望尽可能地将旧标签(第一列)更改为新标签 类“A”-“E”被视为序号,因此我开始为每个用户的更改分配“损失”:例如A->B is loss=1.0,A->C is loss=2.0,等等,并在Python上尝试了一些scipy最小化函数,以最小化整个用户的总损

现在我有了一个表数据,它有50000行(例如表示不同的用户)和两列:一个用于类“a”到“E”的标签,另一个用于小数点为0.0到14000000.00的分数。 我的任务是根据第二列的分数,将一个新标签附加到“a”到“E”的用户上

在这里,我希望尽可能地将旧标签(第一列)更改为新标签

类“A”-“E”被视为序号,因此我开始为每个用户的更改分配“损失”:例如A->B is loss=1.0,A->C is loss=2.0,等等,并在Python上尝试了一些scipy最小化函数,以最小化整个用户的总损失,从而找到AB、BC、CD、DE的四个最佳截止点,但效果不好(从我给出的初始点开始从未改变。),这并不奇怪,因为损失函数是阶跃函数,几乎所有地方都有零梯度

以下是我运行的供您参考的内容。obj3是一个函数,用于计算总损失,输入一个列表,其中四个元素按此顺序表示DE、CD、BC、AB的截止点。由于DE的截止点总是小于CD等的截止点,因此我使用了三个约束条件:

b = (0,max(df['score']))
bounds = (b,b,b,b)

constraints = [
    {'type': 'ineq', 'fun': lambda x: x[1] - x[0]},
    {'type': 'ineq', 'fun': lambda x: x[2] - x[1]},
    {'type': 'ineq', 'fun': lambda x: x[3] - x[2]},
]

sol = minimize(obj3, thres_init, method='SLSQP', bounds=bounds, constraints=constraints) 
sol.x
旧标签和新标签可以进行一些洗牌(如按分数降序排列,旧标签类似于“aabdeabd…”,我想一次性暴力方法可能不一定能很好地工作(我还没有尝试过)

使用排序的唯一分数列表的索引(有5000个唯一分数),我还想到了一些元启发式方法,比如GA(因为现在我们有更小的搜索空间,但它是组合优化),但我不确定是否值得投入时间

有人知道解决这个优化问题的算法吗


提前谢谢。

考虑将类
A->E
值设置为
0->5

Y
为按递增顺序排序的数组分数

例如:
Y=[1,2,5,…,14*1e6]

赋值前,将
X
设为关联的有值类

例如:
X=[0,5,2,3,…1]

假设我们因此希望在位置
c_i
处切割
4
i=0到3
c_j>c_i如果j>i

给定的
c_i
组合的成本是
sum_i(|X[0:c_i]-i)

我们可以编写非常愚蠢的最小化函数(不是有效的python)

相关的动态方法可能类似于

take the first cut as label_0
for all positions
    generate the associated cost of that cut for that position. 
    (you could reuse the cost of the previous calculated position, but it is not the point)

take the next cut (as label_1), for all generated (previous) positions as p, 
    for all positions as new_position > p
        put the new_position's cost as min(new_position's cost, p + cost from p to that new_position)

do so until first cut is placed
At that stage, you must for every position add the remaining class cost
空间复杂度:O(n_分数) 时间复杂度:O(n_分数*n_分数*n_分数)

下面的方法似乎有效,但也没有真正经过测试。所以要小心

X=[0,0,1,1,0,1 ,1, 2, 2, 2 ,1]
Y=[1,2,5,7,8,10,11,11,11,11,12] #just useless

def cost(left, right, label):
    s = 0
    for i in range(left, right+1):
        s += abs(X[i]-label)
    return s

def dp():
    n_scores = len(X)
    positions = [0]*n_scores
    for pos in range(n_scores):
        positions[pos] = {'cost':cost(0, pos, 0), 'cuts':[pos]}

    for label_cut in range(1, max(X)):

        next_positions = [{ 'cost':1e5, 'cuts':[]} for i in range(n_scores)]

        for pos in range(n_scores):
            p = positions[pos]
            for cut in range(pos+1, n_scores-1):
                c = cost(pos+1, cut, label_cut)
                total_cost = c + p['cost']
                next_p = next_positions[cut]
                if total_cost < next_p['cost']:
                    next_positions[cut] = {'cost': total_cost, 'cuts': p['cuts'] + [cut] }
        positions = next_positions

    #print the position for which the cost is minimal
    best = {'cost':1e5}
    for pos in range(n_scores):
        p = positions[pos]
        total_cost = p['cost'] + cost(pos+1, n_scores-1, max(X))
        if total_cost < best['cost']:
            best = {
                'cuts': p['cuts'],
                'cost': total_cost
            }
    return best

X=[0,0,1,1,0,1,1,2,2,2,1]
Y=[1,2,5,7,8,10,11,11,11,12]#没用
def成本(左、右、标签):
s=0
对于范围内的i(左、右+1):
s+=abs(X[i]-标签)
返回s
def dp():
n_分数=len(X)
职位=[0]*n_分数
对于范围内的pos(n_分数):
位置[pos]={'cost':成本(0,pos,0),'cuts':[pos]}
对于标签_切入范围(1,最大值(X)):
下一个_位置=[{‘成本’:1e5,‘切割’:[]}范围内的i(n_分数)]
对于范围内的pos(n_分数):
p=位置[pos]
对于切入范围(位置+1,n_分数-1):
c=成本(位置+1,切割,标签切割)
总成本=c+p[“成本”]
下一个位置=下一个位置[切割]
如果总成本<下一个成本['cost']:
下一个位置[cut]={'cost':总成本,'cuts':p['cuts']+[cut]}
位置=下一个位置
#打印成本最低的职位
最佳={'cost':1e5}
对于范围内的pos(n_分数):
p=位置[pos]
总成本=p[“成本”]+成本(位置+1,n\U得分-1,最大(X))
如果总成本<最佳[成本]:
最佳={
“切割”:p[“切割”],
“成本”:总成本
}
回报最好
X=[0,0,1,1,0,1 ,1, 2, 2, 2 ,1]
Y=[1,2,5,7,8,10,11,11,11,11,12] #just useless

def cost(left, right, label):
    s = 0
    for i in range(left, right+1):
        s += abs(X[i]-label)
    return s

def dp():
    n_scores = len(X)
    positions = [0]*n_scores
    for pos in range(n_scores):
        positions[pos] = {'cost':cost(0, pos, 0), 'cuts':[pos]}

    for label_cut in range(1, max(X)):

        next_positions = [{ 'cost':1e5, 'cuts':[]} for i in range(n_scores)]

        for pos in range(n_scores):
            p = positions[pos]
            for cut in range(pos+1, n_scores-1):
                c = cost(pos+1, cut, label_cut)
                total_cost = c + p['cost']
                next_p = next_positions[cut]
                if total_cost < next_p['cost']:
                    next_positions[cut] = {'cost': total_cost, 'cuts': p['cuts'] + [cut] }
        positions = next_positions

    #print the position for which the cost is minimal
    best = {'cost':1e5}
    for pos in range(n_scores):
        p = positions[pos]
        total_cost = p['cost'] + cost(pos+1, n_scores-1, max(X))
        if total_cost < best['cost']:
            best = {
                'cuts': p['cuts'],
                'cost': total_cost
            }
    return best