0

I am trying to plot my data as a stacked bar chart using the ggplot2 package. I want to:

  1. get the dataframe's row names on the x axis;
  2. sum up the values by month and show the split by each column as well;
  3. order the values in decreasing order for every month.

My data:

neg.trans <- data.frame( Fraud = c(1.686069964, 2.95565648, 
1.170119649,0.429596978),
DeviceDeposit= c( 0.86629,0.61366,0.97226,0.42835),
Usagefees= c(2.2937235,2.294725,2.587091,1.841178),
SecurityDeposit= c(1.616816492, 3.036161258,5.820125209, 2.62082681), 
row.names=c("2018-Oct","2018-Nov","2018-Dec","2019-Jan"))

I'd like to generate a chart that looks like below:

this

Is this possible to do this with R?

Z.Lin
  • 28,055
  • 6
  • 54
  • 94
Arjun Raaghav
  • 79
  • 1
  • 8

2 Answers2

3

Here is an improved handling of the dates and a more base R (well still using ggplot2...) solution:

library(tidyverse)

my.df <- neg.trans %>%
  # Convert the row.names to a proper column so it can be the x-axis
  rownames_to_column("Date") %>% 
  # Format the Date colum with parse_date, %Y is the symbol for year, %b for abbrev. months
  mutate(Date = parse_date(Date, format = "%Y-%b")) %>%
  # Transform the data from wide to long format
  gather("type", "value", -Date)

ggplot(my.df, aes(Date, value, fill = type)) + 
  geom_col() +
  scale_x_date(date_labels = "%Y-%b") # Take care of the correct date-labels

enter image description here

library(ggplot2)

# Convert the row.names to a proper column so it can be the x-axis
neg.trans$Date <- row.names(neg.trans)

# Columns which should be gathered into one 
ids <- c("Fraud", "DeviceDeposit", "Usagefees", "SecurityDeposit")

# Transform the data from wide to long format
my.df <- reshape(neg.trans, idvar = "Date", varying = list(ids), 
                 times = ids, v.names = "value", direction = "long")
row.names(my.df) <- NULL

# Add a day to each Date so we can transform it
my.df$Date <- paste0(my.df$Date, "-01")
# Format the Date colum with as.Date, %Y is for year, %b for abbrev. months, %d for day
my.df$Date <- as.Date(my.df$Date, format = "%Y-%b-%d")

ggplot(my.df, aes(Date, value, fill = time)) + 
  geom_col() +
  scale_x_date(date_labels = "%Y-%b")

enter image description here

Descending odering

If you want to order your columns individually you can do the following (adapted from https://stackoverflow.com/a/53598064/5892059)

my.df <- my.df %>% 
  arrange(Date, type) %>% 
  mutate(type = factor(type)) %>% 
  arrange(Date, -value) 

aux <- with(my.df, match(sort(unique(type)), type))

ggplot(my.df, aes(Date, value, fill = interaction(-value, Date))) + 
  geom_col() + 
  scale_fill_manual(values = scales::hue_pal()(4)[my.df$type],
                    labels = with(my.df, type[aux]), 
                    breaks = with(my.df, interaction(-value, Date)[aux]))  +
  scale_x_date(date_labels = "%Y-%b")

In my opinion that looks confusing.

enter image description here

kath
  • 7,624
  • 17
  • 32
  • OP should probably devise means of ensuring correct entry of dates. This problem turned out more complicated than I initially imagined. – NelsonGon Jan 24 '19 at 14:49
  • Wow this works thanks! But The individual components of every month still has not been arranged in Descending order. For. eg. in 2018-Nov and 2018-Dec Security deposit is higher than usagefees. How to put Security Deposit first for just those 2 months? – Arjun Raaghav Jan 24 '19 at 15:34
  • 1
    @ArjunRaaghav see my edit - but I really think this looks not great – kath Jan 24 '19 at 17:39
0

This? Hopefully someone suggests an edit. The way I've handled the date is really not the best.

     library(tidyverse)
       df<-neg.trans %>% 
  mutate(Date=row.names(.),Day=rep(1,nrow(.)),Date=paste(Date,Day,sep="-0"))
df<-df %>% 
  mutate(Date=as.factor(Date)) 
levels(df$Date)<-c("2018-Oct-01","2018-Nov-01","2018-Dec-01","2019-Jan-01")
df%>% 
  gather("ID","Value",-Date,-Day) %>% 
  select(-Day) %>% 
  ggplot(aes(Date,Value,fill=ID)) + geom_col()

NOTE:

     Months<-sapply(strsplit(as.character(df$Date),"-"),"[[",2)
        Months<-recode(Months,"Dec"=12,"Nov"=11,"Oct"=10,"Jan"=1)
      df %>% 
  mutate(Months=Months,Date=str_remove_all(df$Date,"-.*"),
         Date=make_date(Date,Months,Day),Date=as.factor(Date)) %>% 
  gather("ID","Value",-Date,-Day,-Months) %>% 
  arrange(Date) %>% 
  select(-Day,-Months) %>% 
  ggplot(aes(Date,Value,fill=ID)) + geom_col()
NelsonGon
  • 13,015
  • 7
  • 27
  • 57
  • Not working: geting this error while trying to load tidyverse package: Error: package or namespace load failed for ‘tidyverse’ in loadNamespace(i, c(lib.loc, .libPaths()), versionCheck = vI[[i]]): namespace ‘rlang’ 0.2.0 is already loaded, but >= 0.3.0 is required – Arjun Raaghav Jan 24 '19 at 13:53
  • Load these packages. `tidyr`,`dplyr`,`ggplot2` or reinstall `tidyverse` – NelsonGon Jan 24 '19 at 13:55
  • 1
    Start a fresh R session. Install the `tidyverse` (e.g. using `install.packages("tidyverse")` and then try again. – kath Jan 24 '19 at 14:18
  • 2
    @ArjunRaaghav now you should accept the solution if it solved your problem – kath Jan 24 '19 at 15:31
  • @Kath mostly yes.. thanks.. But I am still unable to order the expenses in descending order for every month. How to do that? – Arjun Raaghav Jan 24 '19 at 16:00