почему возникает ошибка в abs(x) : нечисловой аргумент математической функции в ggplot?

#r #ggplot2

Вопрос:

Вот файл .tsv для следующего сценария.

Исходный код:

 library(ggplot2)
library(dplyr)
library(stringr)


adder_dd_with_yyyy_mm <- function(date) {
  if(str_count(date, "-") == 1) {
    return(paste(date, "01", sep = "-"))
  } else {
    return(date)
  }
}

date_abbreviator <- function(date) {
  month <- strftime(date, format = "%b")
  year <- strftime(date, format = "%y")
  return(paste(month, year, sep = "-"))
}


df <- read.csv("figure_metadata.tsv", sep = "t")

df$date <- df$date %>% lapply(adder_dd_with_yyyy_mm) %>%
  lapply(date_abbreviator)


ggplot(data = df)  
  geom_bar(mapping = aes(x = date, fill = clade))


 

Это гарантирует, что в date столбцах и нет пропущенных значений clade .
Я получаю ошибку Error in abs(x) : non-numeric argument to mathematical function . Кто-нибудь может помочь, пожалуйста? Спасибо.

Комментарии:

1. @r2evans как я могу конвертировать list в data.frame ?

Ответ №1:

Результаты вашего df$date переназначения-это list , а не вектор. Это можно исправить несколькими способами:

 df$date <- df$date %>%
  lapply(adder_dd_with_yyyy_mm) %>%
  sapply(date_abbreviator)
## or
df$date <- df$date %>%
  lapply(adder_dd_with_yyyy_mm) %>%
  lapply(date_abbreviator) %>%
  unlist(.)
 

Оба этих метода сопряжены с небольшим риском (без более глубокого изучения вашей обработки), что, если какая-либо функция вернет объект длины, отличной от 1, переназначение может завершиться неудачей. Однако, если все это пойдет хорошо, то

 ggplot(data = df)  
  geom_bar(mapping = aes(x = date, fill = clade))
 

введите описание изображения здесь

Я предполагаю, что, поскольку ваша ось x является категориальной, метки всегда будут сгруппированы. Возможно, было бы лучше рассматривать их как объект реального Date класса.


Этот процесс немного медленный, в основном потому, что вы работаете по одному $date за раз; R хорошо справляется с выполнением целых векторов за раз, поэтому ваша маркировка может быть лучше с чем-то вроде:

 func1 <- function(x) {
  paste0(x, ifelse(str_count(x, "-") == 1, "-01", ""))
}
func1(head(df$date))
# [1] "2020-04-09" "2020-04-08" "2020-04-20" "2020-06-18" "2020-06-18" "2020-03-13"
 

чтобы выполнить всю первую функцию за один шаг. Второй шаг также может быть векторизован,

 func2 <- function(x) {
  format(as.Date(x), format = "%b-%y")
}
func2(func1(head(df$date)))
# [1] "Apr-20" "Apr-20" "Apr-20" "Jun-20" "Jun-20" "Mar-20"
 

Вероятно, их можно было бы объединить в одну функцию:

 func <- function(x) {
  format(as.Date(paste0(x, ifelse(str_count(x, "-") == 1, "-01", ""))), 
         format = "%b-%y")
}
func(head(df$date))
# [1] "Apr-20" "Apr-20" "Apr-20" "Jun-20" "Jun-20" "Mar-20"
 

В конечном счете, однако, я обычно предпочитаю и рекомендую сохранять объекты, подобные дате, в качестве объектов Date класса и ggplot2 выполнять некоторое форматирование.

 # df <- read.csv(...)
df$date <- as.Date(paste0(df$date, ifelse(nchar(df$date) < 10, "-01", "")))

head(df$date)
# [1] "2020-04-09" "2020-04-08" "2020-04-20" "2020-06-18" "2020-06-18" "2020-03-13"
lubridate::floor_date(head(df$date), unit="months")
# [1] "2020-04-01" "2020-04-01" "2020-04-01" "2020-06-01" "2020-06-01" "2020-03-01"

df$date <- as.Date(lubridate::floor_date(df$date, unit="months"))
head(df$date)
# [1] "2020-04-01" "2020-04-01" "2020-04-01" "2020-06-01" "2020-06-01" "2020-03-01"
class(df$date)
# [1] "Date"

ggplot(data = df)  
  geom_bar(mapping = aes(x = date, fill = clade))  
  scale_x_date(labels = function(z) format(z, format = "%b-%y"))
 

ggplot2, с осью x класса даты

Если вам нужны все галочки оси, то (как указано выше) она будет занята. Мы можем контролировать , какие из них отображаются с помощью breaks= , а затем, при необходимости, поворачивать метки.

 ggplot(data = df)  
  geom_bar(mapping = aes(x = date, fill = clade))  
  scale_x_date(
    breaks = function(z) seq(lubridate::floor_date(z[1], "month"), lubridate::ceiling_date(z[2], "month"), by = "month"),
    labels = function(z) format(z, format = "%b-%y")
  )  
  theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust = 1))
 

ggplot со всеми галочками/метками по оси x, повернутыми

*Отредактировано, чтобы исправить breaks= ошибку. Обратите внимание, что Jan-20 и Aug-21 включены в основном потому, что большинство функций построения немного расширяют оси при работе с числовыми данными.

Комментарии:

1. в январе-20 нет образца. Но почему показан график?

2. есть образцы за 21 июля, но на графике нет полосы.

3. «20 января»: потому ggplot2 что по умолчанию расширяются числовые оси. «21 июля»: ошибка в моем breaks= коде, исправлена через минуту…. смотрите мою правку.