<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Pablo Bernabeu</title>
    <description>The latest articles on DEV Community by Pablo Bernabeu (@pablobernabeu).</description>
    <link>https://dev.to/pablobernabeu</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F324265%2F8fa966ad-4823-4a82-9a11-701ec3858173.png</url>
      <title>DEV Community: Pablo Bernabeu</title>
      <link>https://dev.to/pablobernabeu</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/pablobernabeu"/>
    <language>en</language>
    <item>
      <title>Naive Principal Component Analysis in R</title>
      <dc:creator>Pablo Bernabeu</dc:creator>
      <pubDate>Sun, 26 Jan 2020 08:29:07 +0000</pubDate>
      <link>https://dev.to/pablobernabeu/naive-principal-component-analysis-in-r-1ab1</link>
      <guid>https://dev.to/pablobernabeu/naive-principal-component-analysis-in-r-1ab1</guid>
      <description>&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Principal_component_analysis"&gt;Principal Component Analysis (PCA)&lt;/a&gt; is a technique used to find the core components that underlie different variables. It comes in very useful whenever doubts arise about the true origin of three or more variables. There are two main methods for performing a PCA: naive or less naive. In the naive method, you first check some conditions in your data which will determine the essentials of the analysis. In the less-naive method, you set those yourself based on whatever prior information or purposes you had. The latter method is appropriate when you already have enough information about the intercorrelations, or when you are required to select a specific number of components. I will tackle the naive method, mainly by following the guidelines in &lt;a href="https://freethegeogbooks.files.wordpress.com/2016/08/book-for-r-language-stats.pdf"&gt;Field, Miles, and Field (2012)&lt;/a&gt;, with updated code where necessary. A &lt;a href="https://freethegeogbooks.files.wordpress.com/2016/08/book-for-r-language-stats.pdf"&gt;manual by Charles M. Friel&lt;/a&gt; (Sam Houston State University) was also useful.&lt;/p&gt;

&lt;p&gt;The 'naive' approach is characterized by a first stage that checks whether the PCA should actually be performed with your current variables, or if some should be removed. The variables that are accepted are taken to a second stage which identifies the number of principal components that seem to underlie your set of variables.&lt;/p&gt;

&lt;h4&gt;
  
  
  STAGE 1. Determine whether PCA is appropriate at all, considering the variables
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Variables should be &lt;strong&gt;inter-correlated enough but not too much.&lt;/strong&gt; Field et al. (2012) provide some thresholds, suggesting that no variable should have many correlations below .30, or &lt;em&gt;any&lt;/em&gt; correlation at all above .90. Thus, in the example here, variable Q06 should probably be excluded from the PCA.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--TYW08nxf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://media-exp1.licdn.com/dms/image/C5612AQE7vLkOVSIaVQ/article-inline_image-shrink_1500_2232/0%3Fe%3D1585785600%26v%3Dbeta%26t%3DABp_9l8pA-tyTuMANjTv7nCBPKXSBTm4c8X3ocX7yYs" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--TYW08nxf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://media-exp1.licdn.com/dms/image/C5612AQE7vLkOVSIaVQ/article-inline_image-shrink_1500_2232/0%3Fe%3D1585785600%26v%3Dbeta%26t%3DABp_9l8pA-tyTuMANjTv7nCBPKXSBTm4c8X3ocX7yYs" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Bartlett's test&lt;/strong&gt;, on the nature of the intercorrelations, should be significant. Significance suggests that the variables are not an 'identity matrix' in which correlations are a sampling error.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;KMO&lt;/strong&gt; (Kaiser-Meyer-Olkin), a measure of sampling adequacy based on common variance (so similar purpose as Bartlett's). As Field et al. review, 'values between .5 and .7 are mediocre, values between .7 and .8 are good, values between .8 and .9 are great and values above .9 are superb' (p. 761). There's a general score as well as one per variable. The general one will often be good, whereas the individual scores may more likely fail. Any variable with a score below .5 should probably be removed, and the test should be run again.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Determinant:&lt;/strong&gt; A formula about multicollinearity. The result should preferably fall below .00001.&lt;br&gt;
Note that some of these tests are run on the dataframe and others on a correlation matrix of the data, as distinguished below.&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
# Necessary libraries
library(ltm)
library(lattice)
library(psych)
library(car)
library(pastecs)
library(scales)
library(ggplot2)
library(arules)
library(plyr)
library(Rmisc)
library(GPArotation)
library(gdata)
library(MASS)
library(qpcR)
library(dplyr)
library(gtools)
library(Hmisc)

# Select variables of interest for the PCA
dataset = mydata[, c('select_var1','select_var1','select_var2',
'select_var3',﻿'select_var4','select_var5','select_var6','select_var7')]

﻿# Create matrix﻿: some tests will require it
﻿﻿﻿data_matrix = cor(dataset, use = 'complete.obs')

# S﻿ee intercorrelations
round(data_matrix, 2)

# Bartlett's
﻿cortest.bartlett(dataset)

﻿# KMO (Kaiser-Meyer-Olkin)
KMO(data_matrix)

# Determinant
det(data_matrix)

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h4&gt;
  
  
  STAGE 2. Identify number of components (aka factors)
&lt;/h4&gt;

&lt;p&gt;In this stage, principal components (formally called 'factors' at this stage) are identified among the set of variables.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The identification is done through a basic, 'unrotated' PCA. The number of components set a priori must equal the number of variables that are being tested.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
# Start off with unrotated PCA

pc1 = psych::principal(dataset, nfactors = length(dataset)﻿, rotate="none")
pc1

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Below is an example result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
## Principal Components Analysis
## Call: psych::principal(r = eng_prop, nfactors = 3, rotate = "none")
## Standardized loadings (pattern matrix) based upon correlation matrix
##           PC1   PC2  PC3 h2       u2 com
## Aud_eng -0.89  0.13 0.44  1 -2.2e-16 1.5
## Hap_eng  0.64  0.75 0.15  1  1.1e-16 2.0
## Vis_eng  0.81 -0.46 0.36  1 -4.4e-16 2.0
## 
##                        PC1  PC2  PC3
## SS loadings           1.87 0.79 0.34
## Proportion Var        0.62 0.26 0.11
## Cumulative Var        0.62 0.89 1.00
## Proportion Explained  0.62 0.26 0.11
## Cumulative Proportion 0.62 0.89 1.00
## 
## Mean item complexity =  1.9
## Test of the hypothesis that 3 components are sufficient.
## 
## The root mean square of the residuals (RMSR) is  0 
##  with the empirical chi square  0  with prob &amp;lt;  NA 
## 
## Fit based upon off diagonal values = 1

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Among the columns, there are first the correlations between variables and components, followed by a column (h2) with the &lt;strong&gt;'communalities'&lt;/strong&gt;. If less factors than variables had been selected, communality values would be below 1. Then there is the uniqueness column (u2): &lt;strong&gt;uniqueness&lt;/strong&gt; is equal to 1 minus the communality. Next is 'com', which reflects the &lt;strong&gt;complexity&lt;/strong&gt; with which a variable relates to the principal components. Those components are precisely found below. The first row contains the sums of squared loadings, or eigenvalues, namely, the total variance explained by each linear component. This value corresponds to the number of units explained out of all possible factors (which were three in the above example). The rows below all cut from the same cloth. &lt;em&gt;Proportion var&lt;/em&gt; = variance explained over a total of 1. This is the result of dividing the eigenvalue by the number of components. Multiply by 100 and you get the percentage of total variance explained, which becomes useful. In the example, 99% of the variance has been explained. Aside from the meddling maths, we should actually expect 100% there because the number of factors equaled the number of variables. &lt;em&gt;Cumulative var:&lt;/em&gt; variance added consecutively up to the last component. &lt;em&gt;Proportion explained:&lt;/em&gt; variance explained over what has actually been explained (only when variables = factors is this the same as Proportion var). &lt;em&gt;Cumulative proportion:&lt;/em&gt; the actually explained variance added consecutively up to the last component (Field et al., 2012).&lt;/p&gt;

&lt;p&gt;According to Field et al. (2012), two criteria will determine the number of components to select for the next stage:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Kaiser's criterion: components with SS loadings &amp;gt; 1. In our example, only PC1.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A more lenient alternative is Joliffe's criterion, SS loadings &amp;gt; .7.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Scree plot: the number of points after point of inflexion. For this plot, call:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;plot(pc1$values, type = 'b')
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Imagine a straight line &lt;strong&gt;from the first point on the right.&lt;/strong&gt; Once this line bends considerably, count the points after the bend and up to the last point on the left. The number of points is the number of components to select. The example here is probably the most complicated (two components were finally chosen), but normally it's &lt;a href="https://www.google.nl/search?q=select+principal+components+scree+plot+point+inflexion&amp;amp;source=lnms&amp;amp;tbm=isch&amp;amp;sa=X&amp;amp;ved=0ahUKEwi00ujoto_WAhXJbVAKHbTCBAgQ_AUICigB&amp;amp;biw=1280&amp;amp;bih=619"&gt;not difficult&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--A7FIFwKF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://media-exp1.licdn.com/dms/image/C5612AQF7TVqF5FFS6Q/article-inline_image-shrink_1000_1488/0%3Fe%3D1585785600%26v%3Dbeta%26t%3DbEdWqeoT08j0nSiERX2ZPAlEcyPjUhRsEiucZy3wvBM" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--A7FIFwKF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://media-exp1.licdn.com/dms/image/C5612AQF7TVqF5FFS6Q/article-inline_image-shrink_1000_1488/0%3Fe%3D1585785600%26v%3Dbeta%26t%3DbEdWqeoT08j0nSiERX2ZPAlEcyPjUhRsEiucZy3wvBM" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Based on both criteria, go ahead and select the definitive number of components.&lt;/p&gt;

&lt;h4&gt;
  
  
  STAGE 3. Run definitive PCA
&lt;/h4&gt;

&lt;p&gt;Run a very similar command as you did before, but now with a more advanced method. The first PCA, a heuristic one, worked essentially on the inter-correlations. The definitive PCA, in contrast, will implement a prior shuffling known as 'rotation', to ensure that the result is robust enough (just like cards are shuffled). Explained variance is captured better this way. The go-to rotation method is the orthogonal, or 'varimax' (though others may be considered too).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Now with varimax rotation, Kaiser-normalized by default:
pc2 = psych::principal(dataset, nfactors=2, rotate = "varimax", 
scores = TRUE)
pc2
pc2$loadings

# Healthcheck
pc2$residual
pc2$fit
pc2$communality

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;According to Field et al. (2012), we would want:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Less than half of &lt;strong&gt;residuals&lt;/strong&gt; with absolute values &amp;gt; 0.05&lt;/li&gt;
&lt;li&gt;Model &lt;strong&gt;fit&lt;/strong&gt; &amp;gt; .9&lt;/li&gt;
&lt;li&gt;All &lt;strong&gt;communalities&lt;/strong&gt; &amp;gt; .7&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If any of this fails, consider changing the number of factors. Next, the rotated components that have been 'extracted' from the core of the set of variables can be added to the dataset. This would enable the use of these components as new variables that might prove powerful and useful (as in &lt;a href="http://onlinelibrary.wiley.com/doi/10.1111/j.1551-6709.2010.01157.x/full"&gt;this research&lt;/a&gt;).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dataset = cbind(dataset, pc2$scores)
summary(dataset$RC1, dataset$RC2)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h4&gt;
  
  
  STAGE 4. Determine ascription of each variable to components
&lt;/h4&gt;

&lt;p&gt;Check the main summary by just calling pc2, and see how each variable correlates with the rotated components. This is essential because it reveals how variables load on each component, or in other words, to which component a variable belongs. For instance, the table shown here belongs to a &lt;a href="https://www.linkedin.com/pulse/modality-exclusivity-norms-336-properties-411-dutch-english-bernabeu/?published=t&amp;amp;lipi=urn%3Ali%3Apage%3Ad_flagship3_pulse_read%3BRIskxEq5Rgq59xHemwzpdw%3D%3D"&gt;study about meaning of words&lt;/a&gt;. These results suggest that the visual and haptic modalities of words are quite related, whereas the auditory modality is relatively unique. When the analysis works out well, a cut-off point of &lt;em&gt;r&lt;/em&gt; = .8 may be applied for considering a variable as part of a component.&lt;/p&gt;

&lt;h4&gt;
  
  
  STAGE 5. Enjoy the plot
&lt;/h4&gt;

&lt;p&gt;The plot is perhaps the coolest part about PCA. It really makes an awesome illustration of the power of data analysis.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
ggplot(eng_props,
  aes(RC1, RC2, label = as.character(main_eng))) + stat_density2d (color = "gray87") +
  geom_text(size = ifelse(eng_props$word_eng %in% w_set, 12, 7),
    fontface = ifelse(eng_props$word_eng %in% w_set, 'bold', 'plain')) +
  geom_point(data=eng_props[eng_props$word_eng %in% w_set,], pch=21, fill=NA, size=14, stroke=2, alpha=.6) +
  labs(subtitle='(Data from Lynott &amp;amp; Connell, 2009)', x = "Varimax-rotated Principal Component 1", 
    y = "Varimax-rotated Principal Component 2") +  theme_bw() +   
  theme( plot.background = element_blank(), panel.grid.major = element_blank(),
    panel.grid.minor = element_blank(), panel.border = element_blank(),
    axis.line = element_line(color = 'black'),
    axis.title.x = element_text(colour = 'black', size = 23, margin=margin(15,15,15,15)),
    axis.title.y = element_text(colour = 'black', size = 23, margin=margin(15,15,15,15)),
    axis.text.x = element_text(size=16), axis.text.y  = element_text(size=16),
    plot.title = element_text(hjust = 0.5, size = 32, face = "bold", margin=margin(15,15,15,15)),
    plot.subtitle = element_text(hjust = 0.5, size = 20, margin=margin(2,15,15,15)) ) +
  geom_label_repel(data = eng_props[eng_props$word_eng %in% w_set,], aes(label = word_eng), size = 8, 
    alpha = 0.77, color = 'black', box.padding = 1.5 )

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Below is an example combining PCA plots with code similar to the above. These plots illustrate something further with regard to the relationships among modalities. In property words, the different modalities spread out more clearly than they do in concept words. This makes sense because in language, properties define concepts (&lt;a href="https://www.linkedin.com/pulse/modality-exclusivity-norms-336-properties-411-dutch-english-bernabeu?published=t&amp;amp;lipi=urn%3Ali%3Apage%3Ad_flagship3_pulse_read%3BRIskxEq5Rgq59xHemwzpdw%3D%3D"&gt;see more&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--pmQjfRa2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://media-exp1.licdn.com/dms/image/C4D12AQF1aTAK4IAm9w/article-inline_image-shrink_1500_2232/0%3Fe%3D1585785600%26v%3Dbeta%26t%3D3iXYQJBTSa0elkK9n0Qcnr9CzUt1xOySVsRqxp-XA9s" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--pmQjfRa2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://media-exp1.licdn.com/dms/image/C4D12AQF1aTAK4IAm9w/article-inline_image-shrink_1500_2232/0%3Fe%3D1585785600%26v%3Dbeta%26t%3D3iXYQJBTSa0elkK9n0Qcnr9CzUt1xOySVsRqxp-XA9s" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;An example of these analyses is &lt;a href="https://mybinder.org/v2/gh/pablobernabeu/Modality-exclusivity-norms-747-Dutch-English-replication/master?urlpath=rstudio"&gt;available in available in this RStudio environment&lt;/a&gt;, in the &lt;code&gt;norms.R&lt;/code&gt; script.&lt;/p&gt;

&lt;p&gt;References&lt;/p&gt;

&lt;p&gt;Field, A. P., Miles, J., &amp;amp; Field, Z. (2012). &lt;em&gt;Discovering Statistics Using R&lt;/em&gt;. London, UK: Sage.&lt;/p&gt;

</description>
      <category>pca</category>
      <category>dimensionality</category>
      <category>reduction</category>
      <category>visualisation</category>
    </item>
    <item>
      <title>The case for data dashboards: First steps in R Shiny</title>
      <dc:creator>Pablo Bernabeu</dc:creator>
      <pubDate>Sun, 26 Jan 2020 07:42:18 +0000</pubDate>
      <link>https://dev.to/pablobernabeu/data-visualisation-apps-what-they-add-3ham</link>
      <guid>https://dev.to/pablobernabeu/data-visualisation-apps-what-they-add-3ham</guid>
      <description>&lt;h4&gt;
  
  
  Dashboards for data visualisation, such as &lt;a href="https://rmarkdown.rstudio.com/flexdashboard/"&gt;R Flexdashboard&lt;/a&gt;, &lt;a href="https://shiny.rstudio.com/"&gt;R Shiny&lt;/a&gt; and &lt;a href="https://www.tableau.com/"&gt;Tableau&lt;/a&gt;, allow the interactive exploration of data by means of drop-down lists and checkboxes, with no coding required from the final users. The apps can be useful for both the data analyst and the public.
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--y5loa2Ll--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://media-exp1.licdn.com/dms/image/C5612AQGK2MyRynkKQg/article-inline_image-shrink_1500_2232/0%3Fe%3D1585785600%26v%3Dbeta%26t%3DzUzndOwgBfw72NytYfbHrQG9CD4Nh1PqqoI9YDAFtY0" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--y5loa2Ll--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://media-exp1.licdn.com/dms/image/C5612AQGK2MyRynkKQg/article-inline_image-shrink_1500_2232/0%3Fe%3D1585785600%26v%3Dbeta%26t%3DzUzndOwgBfw72NytYfbHrQG9CD4Nh1PqqoI9YDAFtY0"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Visualisation apps run on internet browsers. This allows for three options: private viewing (useful during analysis), selective sharing (used within work groups), or internet publication. Among the available platforms, &lt;a href="https://shiny.rstudio.com/"&gt;R Shiny&lt;/a&gt; and &lt;a href="https://www.tableau.com/"&gt;Tableau&lt;/a&gt; stand out due to being relatively accessible to new users. Apps serve a broad variety of purposes (see &lt;a href="https://shiny.rstudio.com/gallery/"&gt;this gallery&lt;/a&gt; and &lt;a href="https://www.tableau.com/products/desktop"&gt;this one&lt;/a&gt;). In science and beyond, these apps allow us to go &lt;a href="https://www.frontiersin.org/articles/10.3389/fpsyg.2015.01782/full"&gt;the extra mile in sharing data&lt;/a&gt;. Alongside files and code shared in repositories, we can present the data in a website, in the form of plots or tables. This facilitates the public exploration of each section of the data (groups, participants, trials...) &lt;a href="https://www.nature.com/articles/d41586-019-01506-x"&gt;to anyone interested, and allows researchers to account for their proceeding&lt;/a&gt; in the analysis.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--lCsqZcl8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://media-exp1.licdn.com/dms/image/C4D12AQHYcdpmcmSypg/article-inline_image-shrink_1500_2232/0%3Fe%3D1585785600%26v%3Dbeta%26t%3D0xfTYFRu_OsWN4lkwnO1IonW6HgAuJD79443sf1-4Ms" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--lCsqZcl8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://media-exp1.licdn.com/dms/image/C4D12AQHYcdpmcmSypg/article-inline_image-shrink_1500_2232/0%3Fe%3D1585785600%26v%3Dbeta%26t%3D0xfTYFRu_OsWN4lkwnO1IonW6HgAuJD79443sf1-4Ms"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Publishers and journals highly encourage authors to make the most of their data by facilitating its easy exploration by the readership--even though they don't normally offer options for hosting web visualisations yet.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--uBSCpM5W--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://media-exp1.licdn.com/dms/image/C4D12AQHzuOJOCZBpTA/article-inline_image-shrink_1500_2232/0%3Fe%3D1585785600%26v%3Dbeta%26t%3Dduyn6c5MvUwZHuyua1JDKnPpqlhqpwqV4W5Ye2iDupg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--uBSCpM5W--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://media-exp1.licdn.com/dms/image/C4D12AQHzuOJOCZBpTA/article-inline_image-shrink_1500_2232/0%3Fe%3D1585785600%26v%3Dbeta%26t%3Dduyn6c5MvUwZHuyua1JDKnPpqlhqpwqV4W5Ye2iDupg"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Apps can also prove valuable to those analysing the data. For instance, my app helped me a lot in identifying the extent of noise in a section of the data. Instead of running through a heavy score of code, the drop-down lists of the app let me seamlessly surf through the different sections.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ooDbqrB3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://media-exp1.licdn.com/dms/image/C4D12AQGQ_a0WksT1xQ/article-inline_image-shrink_1500_2232/0%3Fe%3D1585785600%26v%3Dbeta%26t%3DKGu7N65PWyU6Gc4YFxkDluxdhX2kdeYmsjeGWCMbCzY" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ooDbqrB3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://media-exp1.licdn.com/dms/image/C4D12AQGQ_a0WksT1xQ/article-inline_image-shrink_1500_2232/0%3Fe%3D1585785600%26v%3Dbeta%26t%3DKGu7N65PWyU6Gc4YFxkDluxdhX2kdeYmsjeGWCMbCzY" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At a certain point, I found a data section that was consistently noisier than the rest, and eventually I had to discard it from further statistical analyses. Yet, instead of removing that from the app, I maintained it with a note attached. This particular trait in the data was rather salient.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--BjTOPuZq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://media-exp1.licdn.com/dms/image/C4D12AQG5xrI2EYhM1w/article-inline_image-shrink_1500_2232/0%3Fe%3D1585785600%26v%3Dbeta%26t%3DbSeuYdlt1B1qko_-3z27MPPRDOpNJWHAjXS_UIN3bnU" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--BjTOPuZq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://media-exp1.licdn.com/dms/image/C4D12AQG5xrI2EYhM1w/article-inline_image-shrink_1500_2232/0%3Fe%3D1585785600%26v%3Dbeta%26t%3DbSeuYdlt1B1qko_-3z27MPPRDOpNJWHAjXS_UIN3bnU" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Beyond such a salient feature in the data, a visualisation app may also help to spot subtler patterns such as third variables or individual differences.&lt;/p&gt;

&lt;p&gt;There are several platforms for creating apps (e.g., Tableau, D3.js and R Shiny). I focus on R Shiny here for three reasons: it is affordable to use, fairly accessible to new users, and well suited for science as it is based on the R language (see for instance &lt;a href="https://doi.org/10.1080/10691898.2018.1436999"&gt;this article&lt;/a&gt;).&lt;/p&gt;

&lt;h4&gt;
  
  
  How to Shiny
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--zm09dqr7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://media-exp1.licdn.com/dms/image/C4D12AQF9JEbpZQydZQ/article-inline_image-shrink_1000_1488/0%3Fe%3D1585785600%26v%3Dbeta%26t%3DfWrIOYlCowIAIMaj-5GLcE7k_KfAInJtw77T0eL_Mhc" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zm09dqr7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://media-exp1.licdn.com/dms/image/C4D12AQF9JEbpZQydZQ/article-inline_image-shrink_1000_1488/0%3Fe%3D1585785600%26v%3Dbeta%26t%3DfWrIOYlCowIAIMaj-5GLcE7k_KfAInJtw77T0eL_Mhc"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Shiny apps draw on any standard R code that you may already have. This is most commonly plots or tables, but other stuff such as images or Markdown texts are valid too. This is a nice thing to keep in mind when having to create a new app. Part of the job may already be done! The app is distributed among five different areas.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Data file(s)
These are whatever data files you're using (e.g., with csv or rds extensions).&lt;/li&gt;
&lt;/ul&gt;

&lt;h5&gt;
  
  
  1a. &lt;code&gt;server.R&lt;/code&gt; script
&lt;/h5&gt;

&lt;p&gt;The &lt;code&gt;server&lt;/code&gt; script contains the central processes: plots, tables, etc. Code that existed independently of the app app may be brought into this script by slightly adapting it. At the top, call the &lt;code&gt;shiny&lt;/code&gt; library and any others used (e.g., 'ggplot2'), and also read in the data. The snippet below shows the beginning of an example &lt;code&gt;server.R&lt;/code&gt; script.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
# server

library(shiny)
library(ggplot2)

EEG.ParticipantAndElectrode = readRDS('EEG.ParticipantAndElectrode.rds')
EEG.ParticipantAndBrainArea = readRDS('EEG.ParticipantAndBrainArea.rds')
EEG.GroupAndElectrode = readRDS('EEG.GroupAndElectrode.rds')
EEG.OLDGroupAndElectrode = readRDS('EEG.OLDGroupAndElectrode.rds')


server =

shinyServer(

  function(input, output) {

# plot_GroupAndElectrode:
    output$plot_GroupAndElectrode &amp;lt;- renderPlot({

dfelectrode &amp;lt;- aggregate(microvolts ~ electrode*time*condition, 
EEG.GroupAndElectrode[EEG.GroupAndElectrode$RT.based_Groups==input$var.Group,], mean)

df2 &amp;lt;- subset(dfelectrode, electrode == input$var.Electrodes.1)

df2$condition= as.factor(df2$condition)
df2$condition &amp;lt;- gsub('visual2visual', ' Visual / Visual', df2$condition)
df2$condition &amp;lt;- gsub('haptic2visual', ' Haptic / Visual', df2$condition)
df2$condition &amp;lt;- gsub('auditory2visual', ' Auditory / Visual', df2$condition)

df2$time &amp;lt;- as.integer(as.character(df2$time))
colours &amp;lt;- c('firebrick1', 'dodgerblue', 'forestgreen')
# green:visual2visual, blue:haptic2visual, red:auditory2visual

spec_title = paste0('ERP waveforms for ', input$var.Group, ' Group, Electrode ', input$var.Electrodes.1, ' (negative values upward; time windows displayed)')

plot_GroupAndElectrode = ggplot(df2, aes(x=time, y=-microvolts, color=condition)) +
  geom_rect(xmin=160, xmax=216, ymin=7.5, ymax=-8, color = 'grey75', fill='black', alpha=0, linetype='longdash') +
  geom_rect(xmin=270, xmax=370, ymin=7.5, ymax=-8, color = 'grey75', fill='black', alpha=0, linetype='longdash') +
  geom_rect(xmin=350, xmax=550, ymin=8, ymax=-7.5, color = 'grey75', fill='black', alpha=0, linetype='longdash') +
  geom_rect(xmin=500, xmax=750, ymin=7.5, ymax=-8, color = 'grey75', fill='black', alpha=0, linetype='longdash') +
  geom_line(size=1, alpha = 1) + scale_linetype_manual(values=colours) +
  scale_y_continuous(limits=c(-8.38, 8.3), breaks=seq(-8,8,by=1), expand = c(0,0.1)) +
  scale_x_continuous(limits=c(-208,808),breaks=seq(-200,800,by=100), expand = c(0.005,0), labels= c('-200','-100 ms','0','100 ms','200','300 ms','400','500 ms','600','700 ms','800')) +
  ggtitle(spec_title) + theme_bw() + geom_vline(xintercept=0) +
  annotate(geom='segment', y=seq(-8,8,1), yend=seq(-8,8,1), x=-4, xend=8, color='black') +
  annotate(geom='segment', y=-8.2, yend=-8.38, x=seq(-200,800,100), xend=seq(-200,800,100), color='black') +
  geom_segment(x = -200, y = 0, xend = 800, yend = 0, size=0.5, color='black') +
  theme(legend.position = c(0.100, 0.150), legend.background = element_rect(fill='#EEEEEE', size=0),
    axis.title=element_blank(), legend.key.width = unit(1.2,'cm'), legend.text=element_text(size=17),
    legend.title = element_text(size=17, face='bold'), plot.title= element_text(size=20, hjust = 0.5, vjust=2),
    axis.text.y = element_blank(), axis.text.x = element_text(size = 14, vjust= 2.12, face='bold', color = 'grey32', family='sans'),
    axis.ticks=element_blank(), panel.border = element_blank(), panel.grid.major = element_blank(), 
    panel.grid.minor = element_blank(), plot.margin = unit(c(0.1,0.1,0,0), 'cm')) +
  annotate('segment', x=160, xend=216, y=-8, yend=-8, colour = 'grey75', size = 1.5) +
  annotate('segment', x=270, xend=370, y=-8, yend=-8, colour = 'grey75', size = 1.5) +
  annotate('segment', x=350, xend=550, y=-7.5, yend=-7.5, colour = 'grey75', size = 1.5) +
  annotate('segment', x=500, xend=750, y=-8, yend=-8, colour = 'grey75', size = 1.5) +
  scale_fill_manual(name = 'Context / Target trial', values=colours) +
  scale_color_manual(name = 'Context / Target trial', values=colours) +
  guides(linetype=guide_legend(override.aes = list(size=1.2))) +
   guides(color=guide_legend(override.aes = list(size=2.5))) +
# Print y axis labels within plot area:
  annotate('text', label = expression(bold('\u2013' * '3 ' * '\u03bc' * 'V')), x = -29, y = 3, size = 4.5, color = 'grey32', family='sans') +
  annotate('text', label = expression(bold('+3 ' * '\u03bc' * 'V')), x = -29, y = -3, size = 4.5, color = 'grey32', family='sans') +
  annotate('text', label = expression(bold('\u2013' * '6 ' * '\u03bc' * 'V')), x = -29, y = 6, size = 4.5, color = 'grey32', family='sans')

print(plot_GroupAndElectrode)

output$downloadPlot.1 &amp;lt;- downloadHandler(
    filename &amp;lt;- function(file){
    paste0(input$var.Group, ' group, electrode ', input$var.Electrodes.1, ', ', Sys.Date(), '.png')},
    content &amp;lt;- function(file){
            png(file, units='in', width=13, height=5, res=900)
            print(plot_GroupAndElectrode)
            dev.off()},
    contentType = 'image/png')
  } )

# ...

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://osf.io/uj8z4/"&gt;— Whole script&lt;/a&gt;&lt;/p&gt;

&lt;h5&gt;
  
  
  1b. &lt;code&gt;ui.R&lt;/code&gt; script
&lt;/h5&gt;

&lt;p&gt;The &lt;code&gt;ui&lt;/code&gt; script defines the user interface. For instance, a factor column in the data that has multiple categories may be neatly displayed with a drop-down list on the side bar of the website. The interface may present a central plot before by a legend key below. The snippet below shows the beginning of an example &lt;code&gt;ui.R&lt;/code&gt; script.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
# UI

library(shiny)
library(ggplot2)

EEG.GroupAndElectrode = readRDS('EEG.GroupAndElectrode.rds')
EEG.ParticipantAndBrainArea = readRDS('EEG.ParticipantAndBrainArea.rds')
EEG.ParticipantAndElectrode = readRDS('EEG.ParticipantAndElectrode.rds')
EEG.OLDGroupAndElectrode = readRDS('EEG.OLDGroupAndElectrode.rds')


ui =

shinyUI(

   fluidPage(

    tags$head(tags$link(rel='shortcut icon', href='https://image.ibb.co/fXUwzb/favic.png')),  # web favicon
    tags$meta(charset='UTF-8'),
    tags$meta(name='description', content='This R Shiny visualisation dashboard presents data from a psycholinguistic ERP experiment (Bernabeu et al., 2017).'),
    tags$meta(name='keywords', content='R, Shiny, ggplot2, visualisation, data, psycholinguistics, conceptual processing, modality switch, embodied cognition'),
    tags$meta(name='viewport', content='width=device-width, initial-scale=1.0'),

    titlePanel(h3(strong('Waveforms in detail from an ERP experiment on the Conceptual Modality Switch'), a('(Bernabeu et al., 2017)',
    href='https://figshare.com/articles/EEG_study_on_conceptual_modality-switching_Bernabeu_et_al_in_prep_/4210863', target='_blank',
    style = 'color:#3E454E; text-decoration:underline; font-weight:normal'),    align = 'center', style = 'color:black'),

    windowTitle = 'Visualization of ERP waveforms from experiment on Conceptual Modality Switch (Bernabeu et al., 2017)'),


    sidebarLayout(
    sidebarPanel(width = 2,


# Condition 1 for reactivity between tabs and sidebars

   conditionalPanel(
    condition = 'input.tabvals == 1',

    h5(a(strong('See paper, statistics, all data.'), 'Plots by group and brain area shown in paper.',
    href='https://figshare.com/articles/EEG_study_on_conceptual_modality-switching_Bernabeu_et_al_in_prep_/4210863',
    target='_blank'), align = 'center'),
br(),

    selectInput('var.Group', label = 'Group', choices = list('Quick','Slow'), selected = 'Quick'),
    h6('Quick G.: 23 participants'),
    h6('Slow G.: 23 participants'),
br(),

    selectInput('var.Electrodes.1', label = h5(strong('Electrode'), br(), '(see montage below)'),
                  choices = list('1','2','3','4','5','6','7','8','9','10',
            '11','12','13','14','15','16','17','18','19','20','21',
            '22','23','24','25','26','27','28','29','30','31','33',
            '34','35','36','37','38','39','40','41','42','43','44',
            '45','46','47','48','49','50','51','52','53','54','55',
            '56','57','58','59','60'), selected = '30' ),
br(), br(),

    h6('Source code:'),
    h6(strong('-  '), a('server.R', href='https://osf.io/uj8z4/', target='_blank', style = 'text-decoration: underline;')),
    h6(strong('-  '), a('ui.R', href='https://osf.io/8bwcx/', target='_blank', style = 'text-decoration: underline;')),
br(),
    h6(a('CC-By 4.0 License', href='https://osf.io/97unm/', target='_blank'), align = 'center', style = 'text-decoration: underline;'),

br(), br(), br(), br(), br(), br(), br(), br(), br(), br(), br(), br(), br(), br(),
br(), br(), br(), br(), br(), br(), br(), br(), br(), br(), br(), br(), br(), br(), br(), br(), br(), br(),

    h5(a(strong('See paper, statistics, all data.'),
    href='https://figshare.com/articles/EEG_study_on_conceptual_modality-switching_Bernabeu_et_al_in_prep_/4210863',
    target='_blank'), align = 'center'),
br(), br(), br(), br(), br(), br(), br(), br()
),

# ...

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://osf.io/8bwcx"&gt;— Whole script&lt;/a&gt;&lt;/p&gt;

&lt;h5&gt;
  
  
  2. Deployment and logs
&lt;/h5&gt;

&lt;p&gt;This script contains the commands for deploying the app on- or off-line, and for checking the session logs in case of any errors.&lt;/p&gt;

&lt;h5&gt;
  
  
  3. Automatically created folder
&lt;/h5&gt;

&lt;p&gt;When the app is first deployed on the internet, a subfolder is automatically created with the name 'rsconnect'. This folder contains a text file which can be used to modify the URL and the title of the webpage.&lt;/p&gt;

&lt;p&gt;Steps to create a Shiny app from scratch:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://shiny.rstudio.com/articles/shinyapps.html"&gt;&lt;strong&gt;1. Tutorials (link).&lt;/strong&gt; Being open-source software, excellent directions are available through a Google search.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4G4iEBHe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://media-exp1.licdn.com/dms/image/C4D12AQGIWUkkDbIJHw/article-inline_image-shrink_1500_2232/0%3Fe%3D1585785600%26v%3Dbeta%26t%3DhdYkLYrvC3-NHklASAMh4JRL2s7fllQIpzNW8hbsPzA" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4G4iEBHe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://media-exp1.licdn.com/dms/image/C4D12AQGIWUkkDbIJHw/article-inline_image-shrink_1500_2232/0%3Fe%3D1585785600%26v%3Dbeta%26t%3DhdYkLYrvC3-NHklASAMh4JRL2s7fllQIpzNW8hbsPzA" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://shiny.rstudio.com/articles/shinyapps.html"&gt;The core ideas are:&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As mentioned above, create a &lt;code&gt;ui.R&lt;/code&gt; script for the code containing the user interface, and create a &lt;code&gt;server.R&lt;/code&gt; script for the code containing the main content (your plots / tables, etc).&lt;/p&gt;

&lt;p&gt;At the top of both ui.R and server.R scripts, enter the command &lt;code&gt;library(shiny)&lt;/code&gt; and also load any other libraries you're using (e.g., &lt;code&gt;ggplot2&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Test your app by deploying it locally, before launching online. For this purpose, first save the &lt;code&gt;ui&lt;/code&gt; and &lt;code&gt;server&lt;/code&gt; parts independently, as in:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
ui =

 shinyUI(

   fluidPage(

# ...

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then deploy locally by running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;shinyApp(ui, server)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Managing to run the app locally is a great first step before launching online (which may sometimes prove a bit trickier).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://shiny.rstudio.com/articles/shinyapps.html"&gt;&lt;strong&gt;2. User token (link).&lt;/strong&gt; Sign up and read in your private key—just to be done once in a computer.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Go for it.&lt;/strong&gt; After locally testing and saving the two main scripts (&lt;code&gt;ui.R&lt;/code&gt; and &lt;code&gt;server.R&lt;/code&gt;), run &lt;code&gt;deployApp()&lt;/code&gt; to launch the app online.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Bugs and session logs.&lt;/strong&gt; Most often they won't be bugs actually, but fancies, as it were. For instance, some special characters have to get even more special (technically, UTF-8 encoding). For a character such as 'μ', Shiny prefers 'Âμ', or better, the Unicode &lt;code&gt;expression("\u03bc")&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Cling to your logs by calling the line below, which you may keep at hand in your 'Shiny deployer.R' script.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;showLogs(appPath = getwd(), appFile = NULL, appName = NULL, account = NULL,
entries = 50, streaming = FALSE)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At best, the log output will mention any typos and unaccepted characters, pointing to specific lines in your code.&lt;/p&gt;

&lt;p&gt;It may take a couple of intense days to get a first Shiny app running. Although the usual rabbit holes do exist, years of Shiny have already yielded a sizeable body of free resources online (tutorials, blogs, vlogs). Moreover, there's also &lt;a href="https://community.rstudio.com/"&gt;the RStudio Community&lt;/a&gt; and then StackOverflow etc., where you can post any needs/despair. Post your code, log and explanation, and you’ll be rescued out in a couple of days. Long live those contributors.&lt;/p&gt;

&lt;p&gt;It's sometimes enough to upload a bare app, but you might then think it can look better.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5&lt;/strong&gt; (optional). &lt;strong&gt;Advance.&lt;/strong&gt; Use tabs to combine multiple apps on one webpage, use different widgets, include a download option, etc. Tutorials like &lt;a href="https://www.youtube.com/watch?v=Q9sRKkaNveI"&gt;this one on Youtube&lt;/a&gt; can take you there, especially those that provide the code, as in the description of that video. Use those scripts as templates. For example, I made use of tabs on the top of the dashboard in order to keep the side bar from having too many widgets. The appearance of these tabs can be adjusted. More importantly, the inputs in the sidebar can be modified depending on the active tab, by means of 'reactivity' conditions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
mainPanel(

    tags$style(HTML('
        .tabbable &amp;gt; .nav &amp;gt; li &amp;gt; a                       {background-color:white; color:#3E454E}
        .tabbable &amp;gt; .nav &amp;gt; li &amp;gt; a:hover                 {background-color:#002555; color:white}
        .tabbable &amp;gt; .nav &amp;gt; li[class=active] &amp;gt; a         {background-color:#ECF4FF; color:black}
        .tabbable &amp;gt; .nav &amp;gt; li[class=active] &amp;gt; a:hover   {background-color:#E7F1FF; color:black}
    ')),

    tabsetPanel(id='tabvals',

            tabPanel(value=1, h4(strong('Group &amp;amp; Electrode')), br(), plotOutput('plot_GroupAndElectrode'),
            h5(a(strong('See plots with 95% Confidence Intervals'), href='https://osf.io/2tpxn/',
            target='_blank'), style='text-decoration: underline;'), 
            downloadButton('downloadPlot.1', 'Download HD plot'), br(), br(),
            # EEG montage
            img(src='https://preview.ibb.co/n7qiYR/EEG_montage.png', height=500, width=1000)),

            tabPanel(value=2, h4(strong('Participant &amp;amp; Area')), br(), plotOutput('plot_ParticipantAndLocation'),
            h5(a(strong('See plots with 95% Confidence Intervals'), href='https://osf.io/86ch9/',
            target='_blank'), style='text-decoration: underline;'), 
            downloadButton('downloadPlot.2', 'Download HD plot'), br(), br(),
            # EEG montage
            img(src='https://preview.ibb.co/n7qiYR/EEG_montage.png', height=500, width=1000)),

            tabPanel(value=3, h4(strong('Participant &amp;amp; Electrode')), br(), plotOutput('plot_ParticipantAndElectrode'),
            br(), downloadButton('downloadPlot.3', 'Download HD plot'), br(), br(),
            # EEG montage
            img(src='https://preview.ibb.co/n7qiYR/EEG_montage.png', height=500, width=1000)),

            tabPanel(value=4, h4(strong('OLD Group &amp;amp; Electrode')), br(), plotOutput('plot_OLDGroupAndElectrode'),
            h5(a(strong('See plots with 95% Confidence Intervals'), href='https://osf.io/dvs2z/',
            target='_blank'), style='text-decoration: underline;'), 
            downloadButton('downloadPlot.4', 'Download HD plot'), br(), br(),
            # EEG montage
            img(src='https://preview.ibb.co/n7qiYR/EEG_montage.png', height=500, width=1000))
    ),

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;a href="https://shiny.rstudio.com/gallery/"&gt;official Shiny gallery&lt;/a&gt; offers a great array of apps including their code (e.g., &lt;a href="https://shiny.rstudio.com/gallery/kmeans-example.html"&gt;basic example&lt;/a&gt;). Another feature you may add is the option to download your plots, tables, data...&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
# In ui.R script

downloadButton('downloadPlot.1', 'Download HD plot')

#___________________________________________________


# In server.R script

spec_title = paste0('ERP waveforms for ', input$var.Group, ' Group, Electrode ', input$var.Electrodes.1, ' (negative values upward; time windows displayed)')

plot_GroupAndElectrode = ggplot(df2, aes(x=time, y=-microvolts, color=condition)) +
  geom_rect(xmin=160, xmax=216, ymin=7.5, ymax=-8, color = 'grey75', fill='black', alpha=0, linetype='longdash') +
  geom_rect(xmin=270, xmax=370, ymin=7.5, ymax=-8, color = 'grey75', fill='black', alpha=0, linetype='longdash') +
  geom_rect(xmin=350, xmax=550, ymin=8, ymax=-7.5, color = 'grey75', fill='black', alpha=0, linetype='longdash') +
  geom_rect(xmin=500, xmax=750, ymin=7.5, ymax=-8, color = 'grey75', fill='black', alpha=0, linetype='longdash') +
  geom_line(size=1, alpha = 1) + scale_linetype_manual(values=colours) +
  scale_y_continuous(limits=c(-8.38, 8.3), breaks=seq(-8,8,by=1), expand = c(0,0.1)) +
  scale_x_continuous(limits=c(-208,808),breaks=seq(-200,800,by=100), expand = c(0.005,0), labels= c('-200','-100 ms','0','100 ms','200','300 ms','400','500 ms','600','700 ms','800')) +
  ggtitle(spec_title) + theme_bw() + geom_vline(xintercept=0) +
  annotate(geom='segment', y=seq(-8,8,1), yend=seq(-8,8,1), x=-4, xend=8, color='black') +
  annotate(geom='segment', y=-8.2, yend=-8.38, x=seq(-200,800,100), xend=seq(-200,800,100), color='black') +
  geom_segment(x = -200, y = 0, xend = 800, yend = 0, size=0.5, color='black') +
  theme(legend.position = c(0.100, 0.150), legend.background = element_rect(fill='#EEEEEE', size=0),
    axis.title=element_blank(), legend.key.width = unit(1.2,'cm'), legend.text=element_text(size=17),
    legend.title = element_text(size=17, face='bold'), plot.title= element_text(size=20, hjust = 0.5, vjust=2),
    axis.text.y = element_blank(), axis.text.x = element_text(size = 14, vjust= 2.12, face='bold', color = 'grey32', family='sans'),
    axis.ticks=element_blank(), panel.border = element_blank(), panel.grid.major = element_blank(), 
    panel.grid.minor = element_blank(), plot.margin = unit(c(0.1,0.1,0,0), 'cm')) +
  annotate('segment', x=160, xend=216, y=-8, yend=-8, colour = 'grey75', size = 1.5) +
  annotate('segment', x=270, xend=370, y=-8, yend=-8, colour = 'grey75', size = 1.5) +
  annotate('segment', x=350, xend=550, y=-7.5, yend=-7.5, colour = 'grey75', size = 1.5) +
  annotate('segment', x=500, xend=750, y=-8, yend=-8, colour = 'grey75', size = 1.5) +
  scale_fill_manual(name = 'Context / Target trial', values=colours) +
  scale_color_manual(name = 'Context / Target trial', values=colours) +
  guides(linetype=guide_legend(override.aes = list(size=1.2))) +
   guides(color=guide_legend(override.aes = list(size=2.5))) +
# Print y axis labels within plot area:
  annotate('text', label = expression(bold('\u2013' * '3 ' * '\u03bc' * 'V')), x = -29, y = 3, size = 4.5, color = 'grey32', family='sans') +
  annotate('text', label = expression(bold('+3 ' * '\u03bc' * 'V')), x = -29, y = -3, size = 4.5, color = 'grey32', family='sans') +
  annotate('text', label = expression(bold('\u2013' * '6 ' * '\u03bc' * 'V')), x = -29, y = 6, size = 4.5, color = 'grey32', family='sans')

print(plot_GroupAndElectrode)

output$downloadPlot.1 &amp;lt;- downloadHandler(
    filename &amp;lt;- function(file){
    paste0(input$var.Group, ' group, electrode ', input$var.Electrodes.1, ', ', Sys.Date(), '.png')},
    content &amp;lt;- function(file){
            png(file, units='in', width=13, height=5, res=900)
            print(plot_GroupAndElectrode)
            dev.off()},
    contentType = 'image/png')
  } )

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Apps can include any text, such as explanations of any length and web links. For instance, we can link back to the data repository, where the code for the app can be found.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--PPWVETDc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://media-exp1.licdn.com/dms/image/C4D12AQGwmyG6UdLgZg/article-inline_image-shrink_1500_2232/0%3Fe%3D1585785600%26v%3Dbeta%26t%3DqrZzrv5OyEosGhkx6ObpG7FoW0-K4MT3_jr6gZ5RTeE" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--PPWVETDc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://media-exp1.licdn.com/dms/image/C4D12AQGwmyG6UdLgZg/article-inline_image-shrink_1500_2232/0%3Fe%3D1585785600%26v%3Dbeta%26t%3DqrZzrv5OyEosGhkx6ObpG7FoW0-K4MT3_jr6gZ5RTeE" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;An example of a &lt;a href="https://pablobernabeu.shinyapps.io/ERP-waveform-visualization_CMS-experiment/"&gt;Shiny app is available&lt;/a&gt;, which may also be &lt;a href="https://mybinder.org/v2/gh/pablobernabeu/Modality-switch-effects-emerge-early-and-increase-throughout-conceptual-processing/master?urlpath=rstudio"&gt;edited and run in this RStudio environment&lt;/a&gt;, inside the 'Shiny-app' folder.&lt;/p&gt;

&lt;p&gt;The Shiny server (shinyapps.io) allows publishing dashboards built with various frameworks besides Shiny proper. &lt;a href="https://rmarkdown.rstudio.com/flexdashboard/"&gt;Flexdashboard&lt;/a&gt; and &lt;a href="https://rstudio.github.io/shinydashboard/"&gt;Shinydashboard&lt;/a&gt; are two of these frameworks, which have visible advantages over basic Shiny, in terms of layout. An &lt;a href="https://pablobernabeu.shinyapps.io/Dutch-modality-exclusivity-norms/"&gt;example with Flexdashboard is available&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4OB3H8BB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://raw.githubusercontent.com/pablobernabeu/data-is-present/master/dashboard%2520gif.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4OB3H8BB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://raw.githubusercontent.com/pablobernabeu/data-is-present/master/dashboard%2520gif.gif" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Logistics
&lt;/h4&gt;

&lt;p&gt;Memory capacity can become an issue as you go on, which will be flagged in the error logs as: 'Shiny cannot use on-disk bookmarking'. This doesn't necessarily lead you to a paid subscription or to &lt;a href="https://www.r-bloggers.com/alternative-approaches-to-scaling-shiny-with-rstudio-shiny-server-shinyproxy-or-custom-architecture/"&gt;host the website on a custom server&lt;/a&gt;. Try pruning the data file, outsourcing data sections across the five available apps.&lt;/p&gt;

&lt;p&gt;App providers have specific terms of use. To begin, Shiny has a free starter license with limited use, where free apps can handle a certain amount of data, and up to five apps may be created. Beyond that, RStudio offers a wide range of &lt;a href="http://www.shinyapps.io/#_pricing"&gt;subscriptions&lt;/a&gt; starting at $9/month. For its part, Tableau in principle deals only with &lt;a href="https://www.tableau.com/pricing"&gt;subscriptions&lt;/a&gt; from $35/month on. While they offer 1-year licenses to students and instructors for free, these don't include web hosting, unlike Shiny's free plan. &lt;a href="https://www.linkedin.com/pulse/r-shiny-v-tableau-dawn-graphics-anand-gupta?lipi=urn%3Ali%3Apage%3Ad_flagship3_pulse_read%3BCDbB2MVuQA6l%2BRNxwqWzQg%3D%3D"&gt;Further comparisons&lt;/a&gt; of these platforms are available online. Last, I'll just mention a third language, &lt;a href="https://d3js.org/"&gt;D3&lt;/a&gt;, which is powerful, and may also be used &lt;a href="https://rstudio.github.io/r2d3/"&gt;through R&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In the case of very heavy data or frequent public use, if you don't want to host your Shiny app externally, you might consider rendering a PDF with your visualisations instead.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
pdf("List of plots per page", width=13, height=5)
print(plot1)
print(plot2)
# ...
print(plot150)﻿
dev.off()

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;High-resolution plots can be rendered into a PDF document in a snap. Conveniently, all text is indexed, so it can be searched (Ctrl+f / Cmd+f / 🔍) (&lt;a href="https://osf.io/2tpxn/"&gt;see example&lt;/a&gt;). Furthermore, you may also &lt;a href="http://www.ilovepdf.com/"&gt;merge the rendered PDF&lt;/a&gt; with any other documents.&lt;/p&gt;

&lt;p&gt;Summary in &lt;a href="https://www.slideshare.net/PabloBernabeu/presenting-data-interactively-online-using-r-shiny-126064157"&gt;slides available&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Feel free to share any thoughts or questions in a comment below.&lt;/p&gt;

</description>
      <category>dashboard</category>
      <category>r</category>
      <category>shiny</category>
      <category>flexdashboard</category>
    </item>
  </channel>
</rss>
