Material 树——查找迟到的项目(在 Pandas 数据框内)
Material Tree--Finding Late Items (inside a Pandas Dataframe)
好的,所以我需要帮助 and/or 关于如何解决 material 树 on-time/late 问题的建议。
我有一个 pandas 数据框,其中包含 material 棵树 (['Tree'])、树内的不同层级 (['Level'])、部件号 ( ['Part #'])、计划开始日期 (['Sched Start']) 和计划完成日期 (['Sched Fin'])。
import pandas as pd
data = {'Tree': [1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3],
'Level': [1, 2, 2, 3, 1, 2, 3, 4, 1, 2, 3, 2, 3, 2],
'Part #': ['11', '12A', '12B', '12B3',
'21', '22A', '22A3', '22A4',
'31', '32A', '32A3', '32B', '32B3', '32C'],
'Sched Start': pd.to_datetime(['12/01/2020', '11/01/2020', '11/01/2020', '10/01/2020',
'12/01/2020', '11/01/2020', '10/01/2020', '09/01/2020',
'12/01/2020', '11/01/2020', '10/01/2020', '11/01/2020', '10/01/2020', '11/01/2020']),
'Sched Fin': pd.to_datetime(['12/15/2020', '11/15/2020', '12/02/2020', '11/02/2020',
'12/15/2020', '11/15/2020', '11/02/2020', '09/15/2020',
'12/15/2020', '11/15/2020', '10/15/2020', '11/15/2020', '10/15/2020', '11/15/2020'])
}
df = pd. DataFrame(data)
正常的 material 流程是项目提供给下一个更高的组件。例如,一个 3 级项目馈入一个 2 级项目。 2 级项目提供 1 级项目(1 级是不提供任何内容的 top/final 程序集)。可能有多个 2 级馈送单个 1 级。多个 3 级馈送单个 2 级等等。所以在上面的示例代码中(对于树 1):12B3 馈入 12B,12A 和 12B 馈入 11。
无论如何,我需要添加另一列,其中包含项目完成日期与其下一个更高组件的开始日期的比较数据。回到我们上面的例子。 3 级部分 12B3 的完成日期为 11/02/2020——它为开始日期为 11/01/2020 的 12B 提供数据:12B3 迟到。看日期,12B会迟到,12A会准时
较低的组件将始终位于较高的组件之下。
清澈如泥,对吧?
我尝试过的:
好吧,我做了一个遍历每一行的循环的糟糕尝试。它获取级别值然后转到下一行,如果前一行级别 > 当前行级别它将当前行 'Sched Fin' 与前一行 'Sched Start' 进行比较并取得一点成功。当然,当有顺序相同级别的项目时,这一切都会爆炸(例如两个级别 2)。
如有任何帮助,我们将不胜感激。
** edit ** 树是相互独立的。不像关卡那样捆绑在一起。
就像评论中提到的above_c_level一样,通过一两个class来跟踪进食路径会更容易。
对于我的回答,我修改了你的 data
字典,在树级别和后续生产级别之间有分号,以便它们可以更容易地相互比较(即 '1;2B;3'
)。
首先,class 为您的各个零件提供一个 class 将有助于跟踪不同零件之间的进给。
class Part():
def __init__(self, part, level, start, finish):
self.part = part
self.level = level
self.start = pd.to_datetime(start)
self.finish = pd.to_datetime(finish)
self.feedsl = []
self.isfedl = []
self.status = None
def __str__(self):
return '({}, {}, {}, {})'.format(self.part, self.level, self.start, self.finish)
def __repr__(self):
return self.__str__()
def feeds(self, parts):
for p in parts:
p.isfedl.append(self)
self.feedsl.append(p)
def isfed(self, parts):
for p in parts:
self.isfedl.append(p)
p.feedsl.append(self)
def late(self):
deltas = []
for feedp in self.feedsl:
delta = feedp.start - self.finish
deltas.append(delta)
#lates should have self.finish > self.start, so negative delta
lates = [t for t in deltas if t.days < 0]
if len(lates) > 0:
self.status = 'LATE'
self.LATE = True
elif len(lates) == 0:
self.status = 'ONTIME'
self.LATE = False
return self.status
每个部分都会根据您输入的日期来跟踪它是准时还是迟到。您可以指定一个部件馈送到另一个部件(这会同时更新部件的 feedsl
属性和接收者的 isfedl
属性)或指定一个部件由一定数量的部件馈送(再次, 更新两个属性)。我将输入设置为假定一个列表,因此如果您逐一指定很多,您可以修改或将所有内容括在括号中。
执行此操作后,您必须根据数据生成零件列表:
parts = []
LEN = len(data['Tree'])
for i in range(LEN):
treelev = data['Tree'][i]
level = data['Level'][i]
partnum = data['Part #'][i]
start = data['Sched Start'][i]
finish = data['Sched Fin'][i]
parts.append(Part(partnum, level, start, finish))
因此,对于部件列表,您可以使用 Part.part
名称通过列表推导来分隔树(因为格式的第一个值始终是树编号)。
现在您需要一个 class 来接受零件列表(假设它们已正确分类到相应的树中),根据零件名称生成馈送路径。 (这是我想要分号的地方)。
class MaterialTree():
def __init__(self, parts):
self.parts = parts
def setLevTree(self):
self.levels = [p.level for p in self.parts]
for ip in range(len(self.parts)-1):
p = self.parts[ip]
#all twos feed one:
if p.level == 1:
p2s = [p for p in self.parts if p.level == 2]
p.isfed(p2s)
continue
#for each n >= 2, if adjacent is > n, adjacent feeds current
for l in range(2, max(self.levels)+1):
pnext = self.parts[ip+1]
if p.level == pnext.level:
continue
elif p.level == pnext.level-1:
p.isfed([pnext])
def setTree(self):
#number of production levels
self.levels = range(max([p.level for p in self.parts]))
#part names for each level
self.levdct = {l+1:[p.part for p in self.parts if int(p.part.split(';')[-1][0]) == l+1] for l in self.levels}
for ik in self.levels[:-1]: #exclude last level, only feeds second to last
#get names for current base level
namebase = self.levdct[ik+1]
#get names for branches one level up
namebranch = self.levdct[ik+2]
#select parts with names in base
base = [p for p in self.parts if p.part in namebase]
#select parts with names in branch
branch = [p for p in self.parts if p.part in namebranch]
#begin feed:
for b in base:
#if there is no letter in the name, all branches feed this
if not b.part.upper().isupper():
for br in branch:
br.feeds([b])
#if there is a letter in the name,
if b.part.upper().isupper():
#select the letter and use it to compare branches
letts = [i for i in b.part if i.upper().isupper()][0]
#only branches with this letter feed this base
for fbr in [br for br in branch if letts in br.part]:
fbr.feeds([b])
def status(self):
lates = []
for p in self.parts:
lates.append(p.late())
self.status = lates
return self.status
各种 str.upper().isupper()
只是测试不同部分名称中是否存在任何字母。您可以使用它来为您的零件生成状态列表,以添加到数据框并根据需要导出到 Excel。
举个例子:
T1 = [p for p in parts if p.part[0] == '1']
m1 = MaterialTree(T1)
m1.setTree()
print(m1.status())
returns 对我来说 ['ONTIME', 'ONTIME', 'LATE', 'LATE']
.
当然,如果您的零件名称具有不容易解析但应该可行的结构,它可能会变得更复杂。
** 编辑 **:如果馈送结构完全由顺序和级别决定(即,具有递增级别的相邻部分馈送当前部分),那么您可以改用 setLevTree
。它采用这种顺序,但不依赖于零件名称。按照树 2 的相同示例,m.setLevTree()
给了我 ['ONTIME', 'ONTIME', 'LATE', 'ONTIME']
.
好的,所以我需要帮助 and/or 关于如何解决 material 树 on-time/late 问题的建议。
我有一个 pandas 数据框,其中包含 material 棵树 (['Tree'])、树内的不同层级 (['Level'])、部件号 ( ['Part #'])、计划开始日期 (['Sched Start']) 和计划完成日期 (['Sched Fin'])。
import pandas as pd
data = {'Tree': [1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3],
'Level': [1, 2, 2, 3, 1, 2, 3, 4, 1, 2, 3, 2, 3, 2],
'Part #': ['11', '12A', '12B', '12B3',
'21', '22A', '22A3', '22A4',
'31', '32A', '32A3', '32B', '32B3', '32C'],
'Sched Start': pd.to_datetime(['12/01/2020', '11/01/2020', '11/01/2020', '10/01/2020',
'12/01/2020', '11/01/2020', '10/01/2020', '09/01/2020',
'12/01/2020', '11/01/2020', '10/01/2020', '11/01/2020', '10/01/2020', '11/01/2020']),
'Sched Fin': pd.to_datetime(['12/15/2020', '11/15/2020', '12/02/2020', '11/02/2020',
'12/15/2020', '11/15/2020', '11/02/2020', '09/15/2020',
'12/15/2020', '11/15/2020', '10/15/2020', '11/15/2020', '10/15/2020', '11/15/2020'])
}
df = pd. DataFrame(data)
正常的 material 流程是项目提供给下一个更高的组件。例如,一个 3 级项目馈入一个 2 级项目。 2 级项目提供 1 级项目(1 级是不提供任何内容的 top/final 程序集)。可能有多个 2 级馈送单个 1 级。多个 3 级馈送单个 2 级等等。所以在上面的示例代码中(对于树 1):12B3 馈入 12B,12A 和 12B 馈入 11。
无论如何,我需要添加另一列,其中包含项目完成日期与其下一个更高组件的开始日期的比较数据。回到我们上面的例子。 3 级部分 12B3 的完成日期为 11/02/2020——它为开始日期为 11/01/2020 的 12B 提供数据:12B3 迟到。看日期,12B会迟到,12A会准时
较低的组件将始终位于较高的组件之下。
清澈如泥,对吧?
我尝试过的:
好吧,我做了一个遍历每一行的循环的糟糕尝试。它获取级别值然后转到下一行,如果前一行级别 > 当前行级别它将当前行 'Sched Fin' 与前一行 'Sched Start' 进行比较并取得一点成功。当然,当有顺序相同级别的项目时,这一切都会爆炸(例如两个级别 2)。
如有任何帮助,我们将不胜感激。
** edit ** 树是相互独立的。不像关卡那样捆绑在一起。
就像评论中提到的above_c_level一样,通过一两个class来跟踪进食路径会更容易。
对于我的回答,我修改了你的 data
字典,在树级别和后续生产级别之间有分号,以便它们可以更容易地相互比较(即 '1;2B;3'
)。
首先,class 为您的各个零件提供一个 class 将有助于跟踪不同零件之间的进给。
class Part():
def __init__(self, part, level, start, finish):
self.part = part
self.level = level
self.start = pd.to_datetime(start)
self.finish = pd.to_datetime(finish)
self.feedsl = []
self.isfedl = []
self.status = None
def __str__(self):
return '({}, {}, {}, {})'.format(self.part, self.level, self.start, self.finish)
def __repr__(self):
return self.__str__()
def feeds(self, parts):
for p in parts:
p.isfedl.append(self)
self.feedsl.append(p)
def isfed(self, parts):
for p in parts:
self.isfedl.append(p)
p.feedsl.append(self)
def late(self):
deltas = []
for feedp in self.feedsl:
delta = feedp.start - self.finish
deltas.append(delta)
#lates should have self.finish > self.start, so negative delta
lates = [t for t in deltas if t.days < 0]
if len(lates) > 0:
self.status = 'LATE'
self.LATE = True
elif len(lates) == 0:
self.status = 'ONTIME'
self.LATE = False
return self.status
每个部分都会根据您输入的日期来跟踪它是准时还是迟到。您可以指定一个部件馈送到另一个部件(这会同时更新部件的 feedsl
属性和接收者的 isfedl
属性)或指定一个部件由一定数量的部件馈送(再次, 更新两个属性)。我将输入设置为假定一个列表,因此如果您逐一指定很多,您可以修改或将所有内容括在括号中。
执行此操作后,您必须根据数据生成零件列表:
parts = []
LEN = len(data['Tree'])
for i in range(LEN):
treelev = data['Tree'][i]
level = data['Level'][i]
partnum = data['Part #'][i]
start = data['Sched Start'][i]
finish = data['Sched Fin'][i]
parts.append(Part(partnum, level, start, finish))
因此,对于部件列表,您可以使用 Part.part
名称通过列表推导来分隔树(因为格式的第一个值始终是树编号)。
现在您需要一个 class 来接受零件列表(假设它们已正确分类到相应的树中),根据零件名称生成馈送路径。 (这是我想要分号的地方)。
class MaterialTree():
def __init__(self, parts):
self.parts = parts
def setLevTree(self):
self.levels = [p.level for p in self.parts]
for ip in range(len(self.parts)-1):
p = self.parts[ip]
#all twos feed one:
if p.level == 1:
p2s = [p for p in self.parts if p.level == 2]
p.isfed(p2s)
continue
#for each n >= 2, if adjacent is > n, adjacent feeds current
for l in range(2, max(self.levels)+1):
pnext = self.parts[ip+1]
if p.level == pnext.level:
continue
elif p.level == pnext.level-1:
p.isfed([pnext])
def setTree(self):
#number of production levels
self.levels = range(max([p.level for p in self.parts]))
#part names for each level
self.levdct = {l+1:[p.part for p in self.parts if int(p.part.split(';')[-1][0]) == l+1] for l in self.levels}
for ik in self.levels[:-1]: #exclude last level, only feeds second to last
#get names for current base level
namebase = self.levdct[ik+1]
#get names for branches one level up
namebranch = self.levdct[ik+2]
#select parts with names in base
base = [p for p in self.parts if p.part in namebase]
#select parts with names in branch
branch = [p for p in self.parts if p.part in namebranch]
#begin feed:
for b in base:
#if there is no letter in the name, all branches feed this
if not b.part.upper().isupper():
for br in branch:
br.feeds([b])
#if there is a letter in the name,
if b.part.upper().isupper():
#select the letter and use it to compare branches
letts = [i for i in b.part if i.upper().isupper()][0]
#only branches with this letter feed this base
for fbr in [br for br in branch if letts in br.part]:
fbr.feeds([b])
def status(self):
lates = []
for p in self.parts:
lates.append(p.late())
self.status = lates
return self.status
各种 str.upper().isupper()
只是测试不同部分名称中是否存在任何字母。您可以使用它来为您的零件生成状态列表,以添加到数据框并根据需要导出到 Excel。
举个例子:
T1 = [p for p in parts if p.part[0] == '1']
m1 = MaterialTree(T1)
m1.setTree()
print(m1.status())
returns 对我来说 ['ONTIME', 'ONTIME', 'LATE', 'LATE']
.
当然,如果您的零件名称具有不容易解析但应该可行的结构,它可能会变得更复杂。
** 编辑 **:如果馈送结构完全由顺序和级别决定(即,具有递增级别的相邻部分馈送当前部分),那么您可以改用 setLevTree
。它采用这种顺序,但不依赖于零件名称。按照树 2 的相同示例,m.setLevTree()
给了我 ['ONTIME', 'ONTIME', 'LATE', 'ONTIME']
.