如何将员工打卡数据转换为15分钟间隔矩阵?

huangapple go评论78阅读模式
英文:

How to convert employee punch data to a 15-minute interval matrix?

问题

I am attempting to take a table similar to the below raw data example with punch-in and punch-out times and convert it to a table (desired format below) which will allow the data to be easily used in Power BI area charts (this data will be plotted with transaction data). Essentially, I'd like to take raw punch time data and place it on a matrix to then count the number of employees "on the clock" per 15-minute interval.

我正在尝试将类似下面的原始数据示例中的表格与打卡时间转换为表格(下面是期望的格式),以便轻松在Power BI区域图中使用这些数据(这些数据将与交易数据一起绘制)。基本上,我想将原始的打卡时间数据放入矩阵中,然后计算每15分钟间隔内“在岗”的员工数量。

Raw Data:

Employee ID Punch In Punch Out Break In Break Out
1234 9 am 4 pm 12:30 pm 1 pm
1235 9:30 am 5 pm null null
1236 8 am 4 pm 11 am 11:45 am

Desired Format:

Employee ID 8 am 8:15 am 8:30 am 8:45 am 9 am 9:15 am 9:30 am 9:45 am
1234 1 1 1 1
1235 1 1
1236 1 1 1 1 1 1 1 1
英文:

I am attempting to take a table similar to the below raw data example with punch-in and punch-out times and convert it to a table (desired format below) which will allow the data to be easily used in Power BI area charts (this data will be plotted with transaction data). Essentially, I'd like to take raw punch time data and place it on a matrix to then count the number of employees "on the clock" per 15-minute interval.

I am open to the toolset to complete this in the simplest manner. Excel, Python, SQL Server, and Power BI are my strongest platforms. Also, open to a better way of achieving this if there's one out there.

Raw Data:

Employee ID Punch In Punch Out Break In Break Out
1234 9 am 4 pm 12:30 pm 1 pm
1235 9:30 am 5 pm null null
1236 8 am 4 pm 11 am 11:45 am

Desired Format:

Employee ID 8 am 8:15 am 8:30 am 8:45 am 9 am 9:15 am 9:30 am 9:45 am
1234 1 1 1 1
1235 1 1
1236 1 1 1 1 1 1 1 1

答案1

得分: 1

以下是Python解决方案的翻译:

import pandas as pd
from dateutil import parser

# 创建一个带有原始打卡数据的示例DataFrame
data = {
    '员工编号': [1234, 1235, 1236],
    '上班打卡时间': ['上午9点', '上午9点30分', '上午8点'],
    '下班打卡时间': ['下午4点', '下午5点', '下午4点'],
    '午休开始时间': ['中午12点30分', None, '上午11点'],
    '午休结束时间': ['下午1点', None, '上午11点45分']
}

df = pd.DataFrame(data)

# 定义矩阵的时间范围
开始时间 = pd.to_datetime('上午8点').time()
结束时间 = pd.to_datetime('下午5点').time()

# 将开始和结束时间转换为Timestamp对象
开始时间戳 = pd.Timestamp.combine(pd.to_datetime('今天').date(), 开始时间)
结束时间戳 = pd.Timestamp.combine(pd.to_datetime('今天').date(), 结束时间)

# 创建一个以15分钟为间隔的时间范围
时间范围 = pd.date_range(start=开始时间戳, end=结束时间戳, freq='15min').time

# 创建一个带有时间间隔作为列的空矩阵DataFrame
矩阵 = pd.DataFrame(columns=时间范围)

# 遍历原始数据DataFrame中的每一行
for _, 行 in df.iterrows():
    员工编号 = 行['员工编号']

    # 获取员工的上班打卡和下班打卡时间
    try:
        上班打卡时间 = parser.parse(行['上班打卡时间']).time()
        下班打卡时间 = parser.parse(行['下班打卡时间']).time()
    except ValueError:
        print(f"员工编号 {员工编号} 的时间格式无效")
        continue

    # 获取员工的午休开始和午休结束时间(如果不为空)
    午休开始时间 = parser.parse(行['午休开始时间']).time() if 行['午休开始时间'] else None
    午休结束时间 = parser.parse(行['午休结束时间']).time() if 行['午休结束时间'] else None

    # 在矩阵中为员工创建一个空行
    矩阵行 = pd.Series(index=矩阵.columns)

    # 遍历矩阵中的每个时间间隔
    for 时间间隔 in 矩阵.columns:
        # 检查员工是否在该时间间隔内
        if 上班打卡时间 <= 时间间隔 < 下班打卡时间 and (午休开始时间 is None or 午休开始时间 <= 时间间隔 < 午休结束时间):
            矩阵行[时间间隔] = 1

    # 将行添加到矩阵DataFrame中
    矩阵行.name = 员工编号
    矩阵 = 矩阵.append(矩阵行)

# 用空字符串填充NaN值
矩阵 = 矩阵.fillna('')

# 显示所有列
pd.set_option('display.max_columns', None)

# 显示包含1的表格
print(矩阵)

SQL解决方案部分不需要翻译。如果您需要任何其他帮助,请随时告诉我。

英文:

Python Solution

import pandas as pd
from dateutil import parser
# Create a sample DataFrame with the raw punch data
data = {
&#39;Employee ID&#39;: [1234, 1235, 1236],
&#39;Punch In&#39;: [&#39;9 am&#39;, &#39;9:30 am&#39;, &#39;8 am&#39;],
&#39;Punch Out&#39;: [&#39;4 pm&#39;, &#39;5 pm&#39;, &#39;4 pm&#39;],
&#39;Break In&#39;: [&#39;12:30 pm&#39;, None, &#39;11 am&#39;],
&#39;Break Out&#39;: [&#39;1 pm&#39;, None, &#39;11:45 am&#39;]
}
df = pd.DataFrame(data)
# Define the time range for the matrix
start_time = pd.to_datetime(&#39;8:00 am&#39;).time()
end_time = pd.to_datetime(&#39;5:00 pm&#39;).time()
# Convert start and end times to Timestamp objects
start_timestamp = pd.Timestamp.combine(pd.to_datetime(&#39;today&#39;).date(), start_time)
end_timestamp = pd.Timestamp.combine(pd.to_datetime(&#39;today&#39;).date(), end_time)
# Create a time range with 15-minute intervals
time_range = pd.date_range(start=start_timestamp, end=end_timestamp, freq=&#39;15min&#39;).time
# Create an empty matrix DataFrame with time intervals as columns
matrix = pd.DataFrame(columns=time_range)
# Iterate over each row in the raw data DataFrame
for _, row in df.iterrows():
employee_id = row[&#39;Employee ID&#39;]
# Get the punch in and punch out times for the employee
try:
punch_in = parser.parse(row[&#39;Punch In&#39;]).time()
punch_out = parser.parse(row[&#39;Punch Out&#39;]).time()
except ValueError:
print(f&quot;Invalid time format for Employee ID {employee_id}&quot;)
continue
# Get the break in and break out times for the employee
break_in = parser.parse(row[&#39;Break In&#39;]).time() if row[&#39;Break In&#39;] else None
break_out = parser.parse(row[&#39;Break Out&#39;]).time() if row[&#39;Break Out&#39;] else None
# Create an empty row for the employee in the matrix
matrix_row = pd.Series(index=matrix.columns)
# Iterate over each time interval in the matrix
for time_interval in matrix.columns:
# Check if the employee is within the time interval
if punch_in &lt;= time_interval &lt; punch_out and (break_in is None or break_in &lt;= time_interval &lt; break_out):
matrix_row[time_interval] = 1
# Append the row to the matrix DataFrame
matrix_row.name = employee_id
matrix = matrix.append(matrix_row)
# Fill the NaN values with empty strings
matrix = matrix.fillna(&#39;&#39;)
# Show all columns
pd.set_option(&#39;display.max_columns&#39;, None)
# Show table of 1&#39;s
print(matrix)

SQL Solution

-- Create the PunchData table
CREATE TABLE PunchData (
EmployeeID	INT
,PunchIn	VARCHAR(10)
,PunchOut	VARCHAR(10)
,BreakIn	VARCHAR(10)
,BreakOut	VARCHAR(10)
);
-- Insert sample data into the PunchData table
INSERT INTO PunchData (EmployeeID, PunchIn, PunchOut, BreakIn, BreakOut)
VALUES
(1234, &#39;9 am&#39;, &#39;4 pm&#39;, &#39;12:30 pm&#39;, &#39;1 pm&#39;)
,(1235, &#39;9:30 am&#39;, &#39;5 pm&#39;, NULL, NULL)
,(1236, &#39;8 am&#39;, &#39;4 pm&#39;, &#39;11 am&#39;, &#39;11:45 am&#39;);
-- Define a CTE called TimeSlots that generates time values in 15-minute increments
WITH TimeSlots AS (
SELECT CAST(&#39;00:00&#39; AS TIME) AS [_Time]
UNION ALL
SELECT DATEADD(minute, 15, [_Time])
FROM TimeSlots
WHERE [_Time] &lt; CAST(&#39;23:45&#39; AS TIME)
),
-- Define a CTE called Matrix that combines PunchData and TimeSlots
Matrix AS (
SELECT
pd.EmployeeID
,ts.[_Time]
,CASE
-- Check if PunchIn and PunchOut fall within the time slot
-- Check if BreakIn and BreakOut (if not NULL) fall within the time slot
WHEN (
(pd.PunchIn &lt;= ts.[_Time] 
AND pd.PunchOut &gt; ts.[_Time])  
AND (
pd.BreakIn IS NULL 
OR (
pd.BreakIn &lt;= ts.[_Time] 
AND pd.BreakOut &gt; ts.[_Time]
)
) 
) 
THEN &#39;1&#39;
ELSE &#39;0&#39;
END AS SlotValue
FROM
PunchData AS pd
CROSS JOIN
TimeSlots AS ts
)
-- Pivot the Matrix data to generate desired columns for each time slot
SELECT
EmployeeID
,MAX(CASE WHEN [_Time] = &#39;09:00&#39; THEN SlotValue ELSE &#39;&#39; END) AS [09:00]
,MAX(CASE WHEN [_Time] = &#39;09:15&#39; THEN SlotValue ELSE &#39;&#39; END) AS [09:15]
,MAX(CASE WHEN [_Time] = &#39;09:30&#39; THEN SlotValue ELSE &#39;&#39; END) AS [09:30]
,MAX(CASE WHEN [_Time] = &#39;09:45&#39; THEN SlotValue ELSE &#39;&#39; END) AS [09:45]
,MAX(CASE WHEN [_Time] = &#39;10:00&#39; THEN SlotValue ELSE &#39;&#39; END) AS [10:00]
,MAX(CASE WHEN [_Time] = &#39;10:15&#39; THEN SlotValue ELSE &#39;&#39; END) AS [10:15]
,MAX(CASE WHEN [_Time] = &#39;10:30&#39; THEN SlotValue ELSE &#39;&#39; END) AS [10:30]
,MAX(CASE WHEN [_Time] = &#39;10:45&#39; THEN SlotValue ELSE &#39;&#39; END) AS [10:45]
,MAX(CASE WHEN [_Time] = &#39;11:00&#39; THEN SlotValue ELSE &#39;&#39; END) AS [11:00]
,MAX(CASE WHEN [_Time] = &#39;11:15&#39; THEN SlotValue ELSE &#39;&#39; END) AS [11:15]
,MAX(CASE WHEN [_Time] = &#39;11:30&#39; THEN SlotValue ELSE &#39;&#39; END) AS [11:30]
,MAX(CASE WHEN [_Time] = &#39;11:45&#39; THEN SlotValue ELSE &#39;&#39; END) AS [11:45]
,MAX(CASE WHEN [_Time] = &#39;12:00&#39; THEN SlotValue ELSE &#39;&#39; END) AS [12:00]
,MAX(CASE WHEN [_Time] = &#39;12:15&#39; THEN SlotValue ELSE &#39;&#39; END) AS [12:15]
,MAX(CASE WHEN [_Time] = &#39;12:30&#39; THEN SlotValue ELSE &#39;&#39; END) AS [12:30]
,MAX(CASE WHEN [_Time] = &#39;12:45&#39; THEN SlotValue ELSE &#39;&#39; END) AS [12:45]
,MAX(CASE WHEN [_Time] = &#39;13:00&#39; THEN SlotValue ELSE &#39;&#39; END) AS [13:00]
,MAX(CASE WHEN [_Time] = &#39;13:15&#39; THEN SlotValue ELSE &#39;&#39; END) AS [13:15]
,MAX(CASE WHEN [_Time] = &#39;13:30&#39; THEN SlotValue ELSE &#39;&#39; END) AS [13:30]
,MAX(CASE WHEN [_Time] = &#39;13:45&#39; THEN SlotValue ELSE &#39;&#39; END) AS [13:45]
,MAX(CASE WHEN [_Time] = &#39;14:00&#39; THEN SlotValue ELSE &#39;&#39; END) AS [14:00]
,MAX(CASE WHEN [_Time] = &#39;14:15&#39; THEN SlotValue ELSE &#39;&#39; END) AS [14:15]
,MAX(CASE WHEN [_Time] = &#39;14:30&#39; THEN SlotValue ELSE &#39;&#39; END) AS [14:30]
,MAX(CASE WHEN [_Time] = &#39;14:45&#39; THEN SlotValue ELSE &#39;&#39; END) AS [14:45]
,MAX(CASE WHEN [_Time] = &#39;15:00&#39; THEN SlotValue ELSE &#39;&#39; END) AS [15:00]
,MAX(CASE WHEN [_Time] = &#39;15:15&#39; THEN SlotValue ELSE &#39;&#39; END) AS [15:15]
,MAX(CASE WHEN [_Time] = &#39;15:30&#39; THEN SlotValue ELSE &#39;&#39; END) AS [15:30]
,MAX(CASE WHEN [_Time] = &#39;15:45&#39; THEN SlotValue ELSE &#39;&#39; END) AS [15:45]
,MAX(CASE WHEN [_Time] = &#39;16:00&#39; THEN SlotValue ELSE &#39;&#39; END) AS [16:00]
,MAX(CASE WHEN [_Time] = &#39;16:15&#39; THEN SlotValue ELSE &#39;&#39; END) AS [16:15]
,MAX(CASE WHEN [_Time] = &#39;16:30&#39; THEN SlotValue ELSE &#39;&#39; END) AS [16:30]
,MAX(CASE WHEN [_Time] = &#39;16:45&#39; THEN SlotValue ELSE &#39;&#39; END) AS [16:45]
,MAX(CASE WHEN [_Time] = &#39;17:00&#39; THEN SlotValue ELSE &#39;&#39; END) AS [17:00]
,MAX(CASE WHEN [_Time] = &#39;17:15&#39; THEN SlotValue ELSE &#39;&#39; END) AS [17:15]
,MAX(CASE WHEN [_Time] = &#39;17:30&#39; THEN SlotValue ELSE &#39;&#39; END) AS [17:30]
,MAX(CASE WHEN [_Time] = &#39;17:45&#39; THEN SlotValue ELSE &#39;&#39; END) AS [17:45]
-- Add more case statements for each 15-minute increment before/after if needed...
FROM
Matrix
GROUP BY
EmployeeID;

huangapple
  • 本文由 发表于 2023年5月30日 11:01:50
  • 转载请务必保留本文链接:https://go.coder-hub.com/76361363.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定