根据 APL 语言中 "Excel" table 给出的信息创建二进制矩阵

Create a binary matrix based on information given by an "Excel" table in the APL language

我有一个 APL 挑战要解决。 我在 table (Excel) 中收到信息,其中线条代表设备,而列代表一年中的日子。在每个单元格中,我都有关于此设备将接受维护的小时数的信息。

当table单元格的值为0时,表示不进行维护,设备全天工作。当table单元格有非0值时,表示该值代表设备维修时数。

例如,我在下面展示table的一段作为示例:

Equipment / Data 01/jan/20 02/jan/20 03/jan/20
Equipment 1 0 8 0
Equipment 2 0 12 8
Equipment 3 8 12 8

但是,我需要创建一个矩阵,其中列是设备,每一行代表另一行日期的分钟数 table ...并且每一天都应该转换为:24 小时 x 60 分钟 = 1440 行(最佳年份为 527040 行)。并且每个元素对于设备工作的值为 1,对于设备不工作的值为 0。

此外,维护时间的分配也有规则:

  1. 如果单元格的值不是0和之前和之后 日期值为 0,维护开始时间为 00:00。 使用示例 table,这发生在设备 1 上;然后,在 最终矩阵,在第 1 行到 1440 的第 1 列中,值 1 将是 放置;从第 1441 行到第 1921 行(代表 8 点钟的位置) table)先放0的值再放1的值直到 该矩阵的末尾(第 4320 行)。
  2. 如果单元格的值不是 0 并且也是在以后的日期, 时间必须满足。使用示例 table,这发生在 设备2;然后,在最终矩阵中,在第 1 行的第 2 列中 2161,将放置值1;从第 2161 行到第 3361 行 (代表一天12点,另一天8点 table)中连续一天的值0会被放置然后 1 的值直到该矩阵的末尾(第 4320 行)。
  3. 如果单元格的值不是 0 并且也在接下来的两个 天;加入前两天。使用示例 table,会发生这种情况 在设备 3 上;然后,在最终矩阵中,在第 1 行的第 3 列中 960 将放置值 1;从第 961 行到第 2161 行(代表 一天 8 点和连续另一天 12 点 table) 0 的值将被放置;从第 2162 行到第 2880 行 值为 1;从第 2881 行到第 3361 行(代表 8 小时的 最后一天)然后 1 的值直到这个矩阵的末尾(行 4320).

设备可能会停运几天(table 单元格中的值为 24),但只要遵守上述规则就可以了。

我是APL语言的初学者,所以我想到了如下开始:

t ← 3 3 ⍴ 0 8 0 0 12 8 8 12 8   ⍝ just to test, in the real case I will import the excel table
(nc nd) ← ⍴ t                   ⍝ the number of rows in the table will be the number of columns (nc) of the matrix and the number of days will generate the number of its rows (nr)
nr ← nd × 24 × 60               ⍝ calculation of the number of rows
m ← nr nc ⍴ 1                   ⍝ creating the matrix with all values equal to 1

到目前为止,我设法想到了......我对上面代码的开头有几个疑问:

  1. 它如何与矩阵 t 交互,毕竟所有的规则都需要 在矩阵 t 中被研究?
  2. 如何在矩阵 m 中插入值 0,因为我开始了 所有元素都等于 1 的矩阵 m?
  3. 这是解决这个问题的最佳策略吗?

那么,你能帮我解决这个问题吗?

首先,让我们解决您的问题:

  1. how could it interact with matrix t, after all the rules need to be researched in matrix t?

对,可能涉及到这个。我认为直接构造结果比修改一个pre-made矩阵更容易。

  1. how to insert the values 0 in the matrix m, since I started the matrix m with all the elements equal to 1?

修改赋值是你的朋友:m[row;column]←1 会改变特定位。

  1. would this be the best strategy to deal with this problem?

我不这么认为。相反,让我们直接从输入矩阵创建结果矩阵……


为了限制数据量,我们来解决这个问题,假设您的结果矩阵每小时需要一行,而不是每分钟。我们将在最后几分钟进行微不足道的调整。

每天分为两个时段;一个用于维护,一个用于工作。其中一个周期的长度可能为零,但这无关紧要。在结果中,1 表示工作时间单位,0 表示维护时间单位。这意味着每一天都可以用两个二元向量表示,即 time-counts 和 1 0 用于维护前的工作或 0 1 用于维护前的维护。 time-counts中的两个元素加起来就是一天的时间单位总数(现在是24个,以后是1440个)。如果我们让 time-counts 复制 (/) 布尔向量,我们会得到一个 moment-by-moment 指示设备是否工作。

正常情况下,我们希望在工作前进行维护,即0 1,但如果我们明天也有维护,而今天是运行个维护日中的第odd-numbered天,那么我们想调换顺序。

让我们从定义缩放开始,这样我们以后就可以从每天 24 个时间单位移动到 1440 个时间单位:

scale ← 1
unitsPerDay ← 24 × scale
maintUnits ← maintUnits × scale

我们计算每天的工作单元数,为维护天数创建一个布尔矩阵,并查找 1 1 个子数组从哪里开始,这表示明天有维护的天数:

workUnits ← unitsPerDay - maintUnits
maintDay ← × maintUnits
maintTomorrow ← 1 1 ⍷ maintDay

为了找出我们交换了哪几天,我们需要找出 1 的每个 运行 中的 运行ning 奇偶校验。我们可以对数据进行分区并在分区上应用 ≠\¨,但有一种更聪明且性能更高的方法可以做到这一点。从 FinnAPL idiom library,我们找到如何在子向量上应用 ≠\

351. Running parity (≠) over subvectors of Y indicated by X X←B1; Y←B1
≠\Y≠X\A≠¯1↓0,A←X/≠\¯1↓0,Y

但是为了指示子向量从哪里开始,我们需要在 1 的每个 运行 中隔离第一个 1:

633. First ones in groups of ones X←B1
X>¯1↓0,X

为清楚起见,我们将这些实用程序命名为 SegRunParityFirstOfRun。沿着最后一个轴封闭,将我们的矩阵转换为向量的向量,因此我们可以在每个对应的两个向量上应用 SegRunParity。完成后,我们将结果混合 () 返回矩阵。现在我们知道什么时候交换;明天维护的时候,今天是odd-numbered天:

firstOfRun ← FirstOfRun maintDay
oddInRun ← ⊃ (⊂[2] firstOfRun) SegRunParity¨ (⊂[2] maintUnits)
swap ← maintTomorrow ∧ oddInRun

我们构建了两个 3D 数组,具有与输入相同的前导形状(设备数量、天数)但尾轴长度为 2,以适应两个周期(maintenance-then-work 或 work-then-maintenance) 每天。这将允许我们在必要时沿尾轴交换,然后再将两个尾轴组合成一个周期轴。第一个数组将保存用于维护然后工作的时间单位计数(但我们可以交换它们),因此我们通过在轴 2 之后沿新轴层压两个矩阵来构造它。第二个数组保存相应的 activity那个时期,所以它具有相同的形状,每一行都是 0 1(除非我们交换)。然后将每个 3D 数组沿其最后一个轴旋转 0 或 1 步,并使用我们交换矩阵中的相应值:

count ← ,[2 3] swap ⌽[3] maintUnits ,[2.5] workUnits
act ← ,[2 3] swap ⌽[3] ((⍴ maintUnits) , 2) ⍴ 0 1

最后,我们再次将计数的最后一个轴和 activity 矩阵括起来,这样我们就可以在相应的向量之间应用复制,然后再将结果混合回矩阵中。我对/使用了一个覆盖函数,因为我不知道它在APL2000中是一个函数还是一个运算符,如果是一个运算符,那么我们不能直接对它应用¨。由于您需要列而不是行,我们必须转置结果:

work ← ⍉ ⊃ (⊂[2] count) Replicate¨ (⊂[2] act)

这是全部代码:

∇ R←X SegRunParity Y;A
  R←≠\Y≠X\A≠¯1↓0,A←X/≠\¯1↓0,Y
∇

∇ R←FirstOfRun X
  R←X>0 ¯1↓0,X
∇

∇ R←X Replicate Y
  R←X/Y
∇

∇ work←Work maintUnits;scale;unitsPerDay;workUnits;maintDay;maintTomorrow;firstOfRun;oddInRun;swap;count;act

  scale ← 1
  unitsPerDay ← 24 × scale
  maintUnits ← maintUnits × scale
 
  workUnits ← unitsPerDay - maintUnits
  maintDay ← × maintUnits
  maintTomorrow ← 1 1 ⍷ maintDay
    
  firstOfRun ← FirstOfRun maintDay
  oddInRun ← ⊃ (⊂[2] firstOfRun) SegRunParity¨ (⊂[2] maintUnits)
  swap ← maintTomorrow ∧ oddInRun
    
  count ← ,[2 3] swap ⌽[3] maintUnits ,[2.5] workUnits
  act ← ,[2 3] swap ⌽[3] ((⍴ maintUnits) , 2) ⍴ 0 1
  work ← ⍉ ⊃ (⊂[2] count) Replicate¨ (⊂[2] act)
∇

如果我们在左侧装饰两列,包含天数和小时数,并添加一个 header 行表示 天[=97],则更容易验证结果=], 小时, 设备数:

(equips days) ← ⍴ t
dayNums ← 24 / ⍳ days
hourNums ← (days × 24) ⍴ ¯1 + ⍳ 24
header ← 'DH' , ⍳ equips
header ,[1] dayNums , hourNums , Work t

Try it online!

你可能已经猜到了,我们可以通过将scale1调整为60,并对我们的装饰进行相应的调整,将时间单位从小时更改为分钟. Try it online!