1
\$\begingroup\$

I wrote this code for a company to visualize the attendance of its employees in July 2017. I have a folder "attendance" that containing 13 Excels file, a file for every employee. I'm pretty new to R, and I would like some good advice on how I can improve this code.

    ---
    title: "Attendance"
    output: html_document
    ---
#attendance = the time when the employee attend
#attendance1 = the time when the employee must attend
#departure = the time when the employee depart
#departure1 = the time when the employee must depart


    ```{r echo=FALSE,  message=FALSE, warning=FALSE}
    library(readxl)
    library(lubridate)
    library(plyr)
    library(base)
    ```

    ```{r echo=FALSE,  message=FALSE, warning=FALSE}
    average_working_hours <- function(Name){
      Name <- read_excel(sprintf("~/attendance/%s.xlsx", Name))
    av_wh_Name <- mean((Name$departure + hours(12))  - Name$attendance, na.rm = TRUE, trim = 0.1)
    av_wh_Name <- av_wh_Name
    }
    ```


    ```{r echo=FALSE,  message=FALSE, warning=FALSE}
    delay <- function(Name) {
      Name <- read_excel(sprintf("~/attendance/%s.xlsx", Name))
      delay <- Name$attendance - Name$attendance1 
      delay <- mean(delay , na.rm = TRUE)
    units(delay) <- "mins"
    delay}
    ```

    ```{r echo=FALSE,  message=FALSE, warning=FALSE}
    over_time <- function(Name){
      Name <- read_excel(sprintf("~/attendance/%s.xlsx", Name))
    over_time <- Name$departure - Name$departure1
    over_time[over_time< 0] <- NA
    over_time <- mean(over_time, na.rm = TRUE)
    units(over_time) <- "mins"
    over_time
    }
    ```
    ```{r echo=FALSE,  message=FALSE, warning=FALSE}
    Name <- c("Alaa", "Aya_Magdy", "Aya_Omar", "Esam", "Esraa", "Hassan", "Mohamed", "Mahmoud", "Nermin_Fekry", "Noha", "Norhan", "Eman", "Safaa")
    attendance <- data.frame("Name" = Name)
    attendance <- mutate(attendance, average_working_hours = lapply(Name, average_working_hours), delay = lapply(Name, delay), over_time = lapply(Name, over_time))

    ```

    ```{r echo=FALSE,  message=FALSE, warning=FALSE}
    attendance <- mutate(attendance, average_working_hours = as.numeric(average_working_hours))
    ggplot(attendance, aes(x=attendance$Name,y=attendance$average_working_hours)) + 
      ggtitle('working hours in July') + 
      ylab(' working hours') + 
      geom_bar(stat = 'identity', aes(fill = attendance$average_working_hours > 8)) + 
      theme_gray() +scale_fill_manual(values=c("slategray3", "slategray4")) + theme(axis.text.x = element_text(angle = 45, hjust = 1, colour = "royalblue4") )
    ```


    ##The most hard working employee 

    ```{r echo=FALSE,  message=FALSE, warning=FALSE}
    print(Name[attendance$average_working_hours == max(attendance$average_working_hours)])
    ```


    ##Who works more than 8 hours a day
    ```{r echo=FALSE,  message=FALSE, warning=FALSE}
    Name[(attendance$average_working_hours) >= 8]
    ```

    ##Who works less than 8 hours a day
    ```{r echo=FALSE,  message=FALSE, warning=FALSE}
    Name[(attendance$average_working_hours) < 8]
    ```



    ```{r echo=FALSE,  message=FALSE, warning=FALSE}
    attendance <- mutate(attendance, delay = as.numeric(delay))
    library(ggplot2)
    ggplot(attendance, aes(x=attendance$Name,y=attendance$delay)) + 
      ggtitle('Average delay') + 
      ylab('delay in minutes') + 
      geom_bar(stat = 'identity', aes(fill = attendance$delay < 30)) +
      theme_gray() +scale_fill_manual(values=c("slategray4", "slategray3")) + theme(axis.text.x = element_text(angle = 45, hjust = 1, colour = "royalblue4") )
    ```


    ##The most commited

    ```{r echo=FALSE,  message=FALSE, warning=FALSE}
    Name[attendance$delay == min(attendance$delay)]
    ```

    ##Who delay less than 30 minutes

    ```{r echo=FALSE,  message=FALSE, warning=FALSE}
    Name[(attendance$delay <= 30)]
    ```



    ```{r echo=FALSE,  message=FALSE, warning=FALSE}
    attendance <- mutate(attendance, over_time = as.numeric(over_time))
    ggplot(attendance, aes(x=attendance$Name,y=attendance$over_time)) + 
      ggtitle('over time in minutes') + 
      ylab('over time') + 
      geom_bar(stat = 'identity', aes(fill = attendance$over_time > 60)) +
      theme_gray() +scale_fill_manual(values=c("slategray3", "slategray4")) + theme(axis.text.x = element_text(angle = 45, hjust = 1, colour = "royalblue4") )

    ```


    ##Who works overtime more than any other
    ```{r echo=FALSE,  message=FALSE, warning=FALSE, comment= NA}
    Name[attendance$over_time == max(attendance$over_time)]
    ```


    ##Who works overtime more than 1 hour?
    ```{r echo=FALSE,  message=FALSE, warning=FALSE, results='asis'}
    Name[attendance$over_time >= 60]
    ```
\$\endgroup\$

1 Answer 1

2
\$\begingroup\$

I think you can simplify your code quite a bit if you compute all the summary metrics once for all in a single function. This way, you also avoid repetitive calls to read_excel:

attendance_summary <- function(Name) {
  dat <- read_excel(sprintf("~/attendance/%s.xlsx", Name))
  pos <- function(x) x[x > 0]
  avg <- function(x, units, ...) as.numeric(mean(x, na.rm = TRUE, ...), units = units)

  summarize(dat,
    Name          = Name,
    working_hours = avg(departure + hours(12) - attendance, units = "hours", trim = 0.1),
    delay         = avg(attendance - attendance,            units = "mins"),
    over_time     = avg(pos(departure - departure1),        units = "mins")
  )
}

Note how I created the pos and avg function so as to not clobber the summarize code with complex and repetitive function calls.

Another advantage of this approach is that it is very simple to maintain. See how easy it would be to add more metrics to your summary, versus what it would take with your approach.

To create the final data.frame with summary metrics for all people, you can do:

attendance <- do.call(rbind, lapply(Name, attendance_summary))

The rest of your code is ok, only making small suggestions below:

  1. Instead of writing all the names in Name <- c("Alaa", "Aya_Magdy", ...), you could get them from the file names. This would be particularly useful if you had many more employees:

    files <- list.files(path = "~/attendance", pattern = ".xlsx$") Name <- sub(".xlsx$", "", files)

  2. I think ggplot exposes the input data.frame so you don't have to write attendance$ over and over. For example, aes(x=attendance$Name,y=attendance$over_time)) can become aes(x = Name, y = over_time).

  3. To avoid writing x twice, you can use Name[which.max(x)] instead of Name[x == max(x)]

  4. This is less about coding but I would warn about making hasty from some of your summary metrics. One example is the way you computed the overtime. If one person hardly ever does overtime but stays two extra hours every time, he or she would score better than a person who does one hour of overtime every day of the year. You might want to replace the definition of pos by function(x) x * (x > 0) so that days without overtime are now accounted for when computing the mean.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.