为连接两个Python数据框的资源分配订单

问题描述 投票:0回答:1

我有一个数据框问题。我有两个数据框。第一个数据帧包含订单详细信息,第二个数据帧包含线圈详细信息。我需要将订单分配给线圈以满足订单的需求。每个订单都有最小和最大卷重范围,因此在分配订单时,我需要维护最小和最大吨数。例如,假设订单需求为14,最小和最大范围分别为10和14,可用卷材重量为24,那么我需要将24吨卷材分成两部分,每部分12,然后从一个12,我将分配 12,然后从第 2 个 12 开始,我将分配 2 以满足 14 的需求。我的第一个数据帧(订单表示唯一的订单 ID)是

     order   bct  TOP_MIN_COIL_WT  TOP_MAX_COIL_WT  demand
0     o1  bct2                3               25    12.000
1     o2  bct2                3               25    11.340
2     o3  bct2                3               25    9.460
3     o4  bct2                3               25    6.540
4     o5  bct1                4                7    51.000
5     o6  bct1                5                7    51.000
6     o7  bct1                3                5    34.000
7     o8  bct1                3                5    22.260
8     o9  bct1                4                6    17.000
9    o10  bct1                5                7    17.000
10   o11  bct1                3                5    17.000
11   o12  bct1                4                7    6.605

第二个datafarme(mc表示唯一的线圈号)是

     mc   bct  coiltons
0    c1  bct2     24.45
1    c2  bct2     24.10
2    c3  bct1     17.08
3    c4  bct1     17.04
4    c5  bct1     17.03
5    c6  bct1     17.01
6    c7  bct1     16.98
7    c8  bct1     16.98
8    c9  bct1     15.88
9   c10  bct1     15.76
10  c11  bct1     15.72
11  c12  bct1     15.65
12  c13  bct1     15.59
13  c14  bct1     13.16
14  c15  bct1     13.14

为了解决分离问题,我定义了一个函数。我已经写了代码,但是分配不正确。另请注意,我们可以满足超过充分利用线圈的需求(额外 10%)。我的代码如下

import numpy as np
import pandas as pd

def splitNumber(n, a, b):
    if n < a or n < b:
        return 1
    lb = int(np.floor(n/a))
    ub = int(np.floor(n/b))
    for i in range(ub, lb+1):
        s = n/i
        if (s > a) and (s < b):
            return i
    return 1

outputRows = []
for index, row in dfo.iterrows():
    order = row['order']
    bct = row['bct']
    minton = row['TOP_MIN_COIL_WT']
    maxton = row['TOP_MAX_COIL_WT']
    dem = row['demand']

    matchedcoil = dfmc[dfmc['bct'] == bct]

    for index, row in matchedcoil.iterrows():
        coil = row['mc']
        coilt = row['coiltons']
        if (coilt > minton) and (coilt < maxton):
            alloc = min(dem, coilt)
            outputRows.append([order, bct, coil, alloc, coilt, 0])
            dem = dem - alloc
            coilt = coilt - alloc
            dfmc.at[index, 'coiltons'] = coilt
            if dem == 0:
                break
        else:
            noParts = splitNumber(coilt, minton, maxton)
            if noParts > 0:
                alloc = min(dem, coilt / noParts)
                for part in range(1, noParts + 1):
                    outputRows.append([order, bct, f"{coil}_{part}", alloc, coilt, 1])
                dem = dem - alloc
                coilt = coilt - alloc
                dfmc.at[index, 'coiltons'] = coilt
            if dem == 0:
                break
       
outputColumns = ['order', 'typebc', 'coil', 'allocatedQuantity', 'ton', 'flag']
df_output = pd.DataFrame(outputRows, columns=outputColumns)

请帮忙。

python pandas dataframe
1个回答
0
投票

问题出在分裂和分配逻辑上。您可以通过改进

splitNumber
函数以返回指定
min-max
范围内的正确零件数来解决此问题。它更新了主分配循环,以处理线圈被拆分到多个订单的情况,同时仍满足 10% 的超额限制。我建议这样做:

import numpy as np
import pandas as pd

def splitNumber(n, a, b):
    lb = int(np.ceil(n / b))
    ub = int(np.floor(n / a))
    for i in range(lb, ub + 1):
        if a <= n / i <= b:
            return i
    return 1

dfo = pd.DataFrame({
    'order': ['o1', 'o2', 'o3', 'o4', 'o5', 'o6', 'o7', 'o8', 'o9', 'o10', 'o11', 'o12'],
    'bct': ['bct2', 'bct2', 'bct2', 'bct2', 'bct1', 'bct1', 'bct1', 'bct1', 'bct1', 'bct1', 'bct1', 'bct1'],
    'TOP_MIN_COIL_WT': [3, 3, 3, 3, 4, 5, 3, 3, 4, 5, 3, 4],
    'TOP_MAX_COIL_WT': [25, 25, 25, 25, 7, 7, 5, 5, 6, 7, 5, 7],
    'demand': [12, 11.34, 9.46, 6.54, 51, 51, 34, 22.26, 17, 17, 17, 6.605]
})

dfmc = pd.DataFrame({
    'mc': ['c1', 'c2', 'c3', 'c4', 'c5', 'c6', 'c7', 'c8', 'c9', 'c10', 'c11', 'c12', 'c13', 'c14', 'c15'],
    'bct': ['bct2', 'bct2', 'bct1', 'bct1', 'bct1', 'bct1', 'bct1', 'bct1', 'bct1', 'bct1', 'bct1', 'bct1', 'bct1', 'bct1', 'bct1'],
    'coiltons': [24.45, 24.1, 17.08, 17.04, 17.03, 17.01, 16.98, 16.98, 15.88, 15.76, 15.72, 15.65, 15.59, 13.16, 13.14]
})

outputRows = []
for _, order_row in dfo.iterrows():
    order = order_row['order']
    bct = order_row['bct']
    minton = order_row['TOP_MIN_COIL_WT']
    maxton = order_row['TOP_MAX_COIL_WT']
    dem = order_row['demand']
    max_allowable = dem * 1.1

    matched_coils = dfmc[dfmc['bct'] == bct].sort_values('coiltons', ascending=False)

    for coil_idx, coil_row in matched_coils.iterrows():
        coil = coil_row['mc']
        coilt = coil_row['coiltons']

        while coilt > 0 and dem > 0:
            if minton <= coilt <= maxton:
                alloc = min(dem, coilt)
                outputRows.append([order, bct, coil, alloc, coilt, 0])
                dem -= alloc
                coilt -= alloc
            else:
                noParts = splitNumber(coilt, minton, maxton)
                if noParts > 1:
                    part_weight = coilt / noParts
                    alloc = min(dem, part_weight)
                    for part in range(noParts):
                        outputRows.append([order, bct, f"{coil}_{part+1}", alloc, part_weight, 1])
                        dem -= alloc
                        coilt -= alloc
                        if dem <= 0:
                            break
                else:
                    break

            dfmc.at[coil_idx, 'coiltons'] = coilt
            if dem <= 0:
                break

        if dem <= 0:
            break

outputColumns = ['order', 'typebc', 'coil', 'allocatedQuantity', 'ton', 'flag']
df_output = pd.DataFrame(outputRows, columns=outputColumns)

这给出了

   order typebc   coil  allocatedQuantity        ton  flag
0     o1   bct2     c1          12.000000  24.450000     0
1     o2   bct2     c2          11.340000  24.100000     0
2     o3   bct2     c2           9.460000  12.760000     0
3     o4   bct2     c1           6.540000  12.450000     0
4     o5   bct1   c3_1           5.693333   5.693333     1
5     o5   bct1   c3_2           5.693333   5.693333     1
6     o5   bct1   c3_3           5.693333   5.693333     1
7     o5   bct1   c4_1           5.680000   5.680000     1
8     o5   bct1   c4_2           5.680000   5.680000     1
9     o5   bct1   c4_3           5.680000   5.680000     1
10    o5   bct1   c5_1           5.676667   5.676667     1
11    o5   bct1   c5_2           5.676667   5.676667     1
12    o5   bct1   c5_3           5.676667   5.676667     1
13    o6   bct1   c6_1           5.670000   5.670000     1
14    o6   bct1   c6_2           5.670000   5.670000     1
15    o6   bct1   c6_3           5.670000   5.670000     1
16    o6   bct1   c7_1           5.660000   5.660000     1
17    o6   bct1   c7_2           5.660000   5.660000     1
18    o6   bct1   c7_3           5.660000   5.660000     1
19    o6   bct1   c8_1           5.660000   5.660000     1
20    o6   bct1   c8_2           5.660000   5.660000     1
21    o6   bct1   c8_3           5.660000   5.660000     1
22    o6   bct1   c9_1           0.030000   5.293333     1
23    o7   bct1   c9_1           3.962500   3.962500     1
...
44   o11   bct1  c14_1           4.745000   4.745000     1
45   o11   bct1  c14_2           4.745000   4.745000     1
46   o11   bct1  c13_1           3.897500   3.897500     1
47   o11   bct1  c13_2           3.897500   3.897500     1
© www.soinside.com 2019 - 2024. All rights reserved.