From 8a07434c806064574f7a3737ade5116a5924496d Mon Sep 17 00:00:00 2001 From: Tyler Date: Tue, 13 Oct 2015 22:16:55 -0400 Subject: [PATCH] added plot method --- DESCRIPTION | 2 +- NAMESPACE | 1 + R/formality.R | 138 +++++++++++++++++++++++++++++- README.Rmd | 27 +++++- README.md | 61 ++++++++----- inst/figure/unnamed-chunk-6-1.png | Bin 0 -> 18490 bytes man/formality.Rd | 3 + man/plot.Formality.Rd | 23 +++++ 8 files changed, 226 insertions(+), 29 deletions(-) create mode 100644 inst/figure/unnamed-chunk-6-1.png create mode 100644 man/plot.Formality.Rd diff --git a/DESCRIPTION b/DESCRIPTION index e250f1d..68de06c 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -6,7 +6,7 @@ Authors@R: c(person("Tyler", "Rinker", email = "tyler.rinker@gmail.com", role = Maintainer: Tyler Rinker Description: Calculate the formality of text based on part of speech tags. Depends: R (>= 3.2.2) -Imports: data.table, tagger +Imports: data.table, ggplot2, gridExtra, grid, tagger Suggests: testthat Date: 2015-10-13 License: GPL-2 diff --git a/NAMESPACE b/NAMESPACE index 9675d0c..a819986 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -2,5 +2,6 @@ S3method(formality,Formality) S3method(formality,default) +S3method(plot,Formality) export(formality) importFrom(data.table,":=") diff --git a/R/formality.R b/R/formality.R index fb54a9c..f57411d 100644 --- a/R/formality.R +++ b/R/formality.R @@ -39,6 +39,9 @@ #' data(presidential_debates_2012) #' (form1 <- with(presidential_debates_2012, formality(dialogue, person))) #' with(presidential_debates_2012, formality(form1, list(person, time))) #recycle form 1 for speed +#' +#' plot(form1) +#' plot(with(presidential_debates_2012, formality(form1, list(person, time)))) formality <- function(text.var, grouping.var = NULL, order.by.formality = TRUE, ...){ UseMethod("formality") @@ -83,8 +86,8 @@ formality.default <- function(text.var, grouping.var = NULL, order.by.formality } } - formal <- c('noun', 'adjective', 'preposition', 'article') - contextual <- c('pronoun', 'verb', 'adverb', 'interjection') + formal <- c('noun', 'preposition', 'adjective', 'article') + contextual <- c('verb', 'pronoun', 'adverb', 'interjection') ## in other version this will be extracted #============================================= @@ -166,8 +169,8 @@ formality.Formality <- function(text.var, grouping.var = NULL, order.by.formalit } } - formal <- c('noun', 'adjective', 'preposition', 'article') - contextual <- c('pronoun', 'verb', 'adverb', 'interjection') + formal <- c('noun', 'preposition', 'adjective', 'article') + contextual <- c('verb', 'pronoun', 'adverb', 'interjection') counts <- attributes(text.var)[["counts"]][["counts"]] @@ -191,3 +194,130 @@ formality.Formality <- function(text.var, grouping.var = NULL, order.by.formalit out } + + + +#' Plots a Formality Object +#' +#' Plots a Formality object. +#' +#' @param x The Formality object +#' @param plot logical. If \code{TRUE} the output is plotted. +#' @param \ldots ignored. +#' @return Returns a list of the three \pkg{ggplot2} objects that make the +#' combined plot. +#' @importFrom data.table := +#' @method plot Formality +#' @export +plot.Formality <- function(x, plot = TRUE, ...){ + + group.vars <- n <- warn <- contextual <- formal <- type <- NULL + + grps <- attr(x, "group.var") + pos <- attr(x, "pos.vars") + + ## Prepare the pos data + express1 <- paste0("lapply(list(", paste(pos, collapse=","), "), function(y) as.numeric(y/n))") + express2 <- paste0("paste(", paste(grps, collapse=", "), ", sep = \"_\")") + pos_dat <- x[, c(grps, pos, "n"), with=FALSE][, + (pos) := eval(parse(text=express1))][, + 'group.vars' := eval(parse(text=express2))][, + 'group.vars' := factor(group.vars, levels=rev(group.vars))][, + c(pos, "n", "group.vars"), with = FALSE] + + pos_dat_long <- data.table::melt(pos_dat, id = c("group.vars", "n"), + variable.name = "pos", value.name = "proportion")[, + pos := factor(pos, levels = attr(x, "pos.vars"))] + + ## prepare the formality data + form_dat <- x[, c(grps, "n", "F"), with=FALSE][, + 'group.vars' := eval(parse(text=express2))][, + 'group.vars' := factor(group.vars, levels=rev(group.vars))][, + c("group.vars", "n", "F"), with = FALSE][, + warn := ifelse(n > 300, FALSE, TRUE)] + + ## prepare the contectual/formal data + con_form_dat <- x[, c(grps, "contextual", "formal", "n"), with=FALSE][, + (c("contextual", "formal")) := list(contextual/n, formal/n)][, + 'group.vars' := eval(parse(text=express2))][, + 'group.vars' := factor(group.vars, levels=rev(group.vars))][, + c("contextual", "formal", "n", "group.vars"), with = FALSE] + + con_form_long <- data.table::melt(con_form_dat, id = c("group.vars", "n"), + variable.name = "type", value.name = "proportion")[, + type := factor(type, levels = c("formal", "contextual"))] + + con_form_plot <- ggplot2::ggplot(con_form_long, + ggplot2::aes_string(x = "group.vars", weight = "proportion", fill ="type")) + + ggplot2::geom_bar() + + ggplot2::coord_flip() + + ggplot2::xlab(NULL) + + ggplot2::ylab("") + + ggplot2::theme_bw() + + ggplot2::theme( + panel.grid = ggplot2::element_blank(), + #legend.position="bottom", + legend.title = ggplot2::element_blank(), + panel.border = ggplot2::element_blank(), + axis.line = ggplot2::element_line(color="grey70") + ) + + ggplot2::scale_y_continuous(labels=function(x) paste0(round(x*100, 0), "%"), + expand = c(0,0)) + + ggplot2::scale_fill_manual(values=pals[c(2, 6), 2]) + + form_plot <- ggplot2::ggplot(form_dat, + ggplot2::aes_string(y = "group.vars", x = "F")) + + ggplot2::geom_point(ggplot2::aes_string(size="n"), alpha=.22) + + ggplot2::scale_size(range=c(1, 7), name = "Text\nLength") + + ggplot2::geom_point(ggplot2::aes_string(color="warn"), size=1.5) + + ggplot2::scale_color_manual(values=c("black", "red"), guide=FALSE) + + ggplot2::ylab(NULL) + + ggplot2::xlab("F Measure") + + ggplot2::theme_bw() + + ggplot2::theme( + #legend.position="bottom", + axis.title.x = ggplot2::element_text(size=11), + #legend.title = ggplot2::element_blank(), + panel.border = ggplot2::element_blank(), + axis.line = ggplot2::element_line(color="grey70") + ) + + pos_heat_plot <- ggplot2::ggplot(pos_dat_long, + ggplot2::aes_string(y = "group.vars", x = "pos", fill="proportion")) + + ggplot2::geom_tile() + + ggplot2::scale_fill_gradient( + labels=function(x) paste0(round(x*100, 0), "%"), + high="#BF812D", + low="white", + name = ggplot2::element_blank() + )+ + ggplot2::ylab(NULL) + + ggplot2::xlab("Part of Speech") + + ggplot2::theme_bw() + + ggplot2::theme( + panel.grid = ggplot2::element_blank(), + #legend.position="bottom", + axis.title.x = ggplot2::element_text(size=11), + legend.title = ggplot2::element_blank(), + panel.border = ggplot2::element_rect(color="grey88") + ) + + ggplot2::guides(fill = ggplot2::guide_colorbar(barwidth = .5, barheight = 10)) #+ + #ggplot2::guides(fill = ggplot2::guide_colorbar(barwidth = 14, barheight = .5)) + + plotout1 <- gridExtra::arrangeGrob(con_form_plot, form_plot, + widths = grid::unit(c(.5, .5), "native"), ncol=2) + + plotout2 <- gridExtra::arrangeGrob(plotout1, pos_heat_plot, ncol=1) + if (isTRUE(plot)) gridExtra::grid.arrange(plotout2) + return(invisible(list(formality = form_plot, contextual_formal = con_form_plot, pos = pos_heat_plot))) +} + + +pals <- structure(list(pos = c("noun", "adjective", "preposition", "article", + "pronoun", "verb", "adverb", "interjection"), cols = c("#8C510A", + "#BF812D", "#DFC27D", "#F6E8C3", "#C7EAE5", "#80CDC1", "#35978F", + "#01665E")), .Names = c("pos", "cols"), row.names = c(NA, -8L + ), class = "data.frame") + + + diff --git a/README.Rmd b/README.Rmd index 164a270..052fb62 100644 --- a/README.Rmd +++ b/README.Rmd @@ -6,7 +6,8 @@ output: toc: true --- -```{r, echo=FALSE} +```{r, echo=FALSE, message=FALSE, warning=FALSE} +library(knitr) desc <- suppressWarnings(readLines("DESCRIPTION")) regex <- "(^Version:\\s+)(\\d+\\.\\d+\\.\\d+)" loc <- grep(regex, desc) @@ -14,7 +15,17 @@ ver <- gsub(regex, "\\2", desc[loc]) verbadge <- sprintf('Version

', ver, ver) ```` -[![Project Status: Wip - Initial development is in progress, but there has not yet been a stable, usable release suitable for the public.](http://www.repostatus.org/badges/0.1.0/wip.svg)](http://www.repostatus.org/#wip) +```{r, echo=FALSE} +knit_hooks$set(htmlcap = function(before, options, envir) { + if(!before) { + paste('

',options$htmlcap,"

",sep="") + } + }) +knitr::opts_knit$set(self.contained = TRUE, cache = FALSE) +knitr::opts_chunk$set(fig.path = "inst/figure/") +``` + +[![Project Status: Active - The project has reached a stable, usable state and is being actively developed.](http://www.repostatus.org/badges/0.1.0/active.svg)](http://www.repostatus.org/#active) [![Build Status](https://travis-ci.org/trinker/formality.svg?branch=master)](https://travis-ci.org/trinker/formality) [![Coverage Status](https://coveralls.io/repos/trinker/formality/badge.svg?branch=master)](https://coveralls.io/r/trinker/formality?branch=master) `r verbadge` @@ -103,4 +114,16 @@ This will take ~20 seconds because of the part of speech tagging that must be un with(presidential_debates_2012, formality(form1, list(time, person))) ``` +## Plotting + +The generic `plot` function provides three views of the data: +1. A filled bar plot of formal vs. contextual usage +2. A dotplot of formality\*\* +3. A heatmap of the usage of the parts of speech used to calculate the formality score + +\*\****Note*** *red dot in center is a warning of less than 300 words* + +```{r} +plot(form1) +```` diff --git a/README.md b/README.md index a579f82..a9b2d9c 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,9 @@ formality ============ -[![Project Status: Wip - Initial development is in progress, but there -has not yet been a stable, usable release suitable for the -public.](http://www.repostatus.org/badges/0.1.0/wip.svg)](http://www.repostatus.org/#wip) +[![Project Status: Active - The project has reached a stable, usable +state and is being actively +developed.](http://www.repostatus.org/badges/0.1.0/active.svg)](http://www.repostatus.org/#active) [![Build Status](https://travis-ci.org/trinker/formality.svg?branch=master)](https://travis-ci.org/trinker/formality) [![Coverage @@ -33,6 +33,7 @@ Table of Contents - [Load the Tools/Data](#load-the-toolsdata) - [Assessing Formality](#assessing-formality) - [Recycling the First Run](#recycling-the-first-run) + - [Plotting](#plotting) Formality Equation ============ @@ -126,13 +127,13 @@ smaller text Heylighen & Dewaele (2002) state: form1 <- with(presidential_debates_2012, formality(dialogue, person)) form1 - ## person noun adjective preposition article pronoun verb adverb - ## 1: QUESTION 155 70 91 38 77 112 26 - ## 2: LEHRER 182 93 104 62 101 164 48 - ## 3: SCHIEFFER 347 176 209 102 211 342 69 - ## 4: ROMNEY 4406 2346 3178 1396 2490 4676 1315 - ## 5: OBAMA 3993 1935 2909 1070 2418 4593 1398 - ## 6: CROWLEY 387 135 269 104 249 405 134 + ## person noun preposition adjective article verb pronoun adverb + ## 1: QUESTION 155 91 70 38 112 77 26 + ## 2: LEHRER 182 104 93 62 164 101 48 + ## 3: SCHIEFFER 347 209 176 102 342 211 69 + ## 4: ROMNEY 4406 3178 2346 1396 4676 2490 1315 + ## 5: OBAMA 3993 2909 1935 1070 4593 2418 1398 + ## 6: CROWLEY 387 269 135 104 405 249 134 ## interjection formal contextual n F ## 1: 4 354 219 573 61.78010 ## 2: 8 441 321 762 57.87402 @@ -150,17 +151,17 @@ time to a fraction of the first run. with(presidential_debates_2012, formality(form1, list(time, person))) - ## time person noun adjective preposition article pronoun verb - ## 1: time 2 QUESTION 155 70 91 38 77 112 - ## 2: time 1 LEHRER 182 93 104 62 101 164 - ## 3: time 1 ROMNEY 950 483 642 286 504 978 - ## 4: time 3 ROMNEY 1766 958 1388 617 1029 1920 - ## 5: time 3 SCHIEFFER 347 176 209 102 211 342 - ## 6: time 2 ROMNEY 1690 905 1148 493 957 1778 - ## 7: time 3 OBAMA 1546 741 1185 432 973 1799 - ## 8: time 1 OBAMA 792 357 579 219 452 925 - ## 9: time 2 OBAMA 1655 837 1145 419 993 1869 - ## 10: time 2 CROWLEY 387 135 269 104 249 405 + ## time person noun preposition adjective article verb pronoun + ## 1: time 2 QUESTION 155 91 70 38 112 77 + ## 2: time 1 LEHRER 182 104 93 62 164 101 + ## 3: time 1 ROMNEY 950 642 483 286 978 504 + ## 4: time 3 ROMNEY 1766 1388 958 617 1920 1029 + ## 5: time 3 SCHIEFFER 347 209 176 102 342 211 + ## 6: time 2 ROMNEY 1690 1148 905 493 1778 957 + ## 7: time 3 OBAMA 1546 1185 741 432 1799 973 + ## 8: time 1 OBAMA 792 579 357 219 925 452 + ## 9: time 2 OBAMA 1655 1145 837 419 1869 993 + ## 10: time 2 CROWLEY 387 269 135 104 405 249 ## adverb interjection formal contextual n F ## 1: 26 4 354 219 573 61.78010 ## 2: 48 8 441 321 762 57.87402 @@ -171,4 +172,20 @@ time to a fraction of the first run. ## 7: 522 4 3904 3298 7202 54.20716 ## 8: 281 2 1947 1660 3607 53.97838 ## 9: 595 7 4056 3464 7520 53.93617 - ## 10: 134 0 895 788 1683 53.17885 \ No newline at end of file + ## 10: 134 0 895 788 1683 53.17885 + +Plotting +-------- + +The generic `plot` function provides three views of the data: + +1. A filled bar plot of formal vs. contextual usage +2. A dotplot of formality\*\* +3. A heatmap of the usage of the parts of speech used to calculate the + formality score + +\*\****Note*** *red dot in center is a warning of less than 300 words* + + plot(form1) + +![](inst/figure/unnamed-chunk-6-1.png) \ No newline at end of file diff --git a/inst/figure/unnamed-chunk-6-1.png b/inst/figure/unnamed-chunk-6-1.png new file mode 100644 index 0000000000000000000000000000000000000000..697f1a7dba9a7459b3b0d7e0876caaf687dc9bcd GIT binary patch literal 18490 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 ri&o;_zkHMOf0&a0^FMl%bk3M@bPyTcrJM|+CJ