title: How to return an 'untidy' dataframe summary of multiple standard statistics arranged by row for each column of a dataframe in R?
body: <p>With Pandas in Python there is the describe() function that returns the summary statistics for a dataframe.  The output is not in a 'tidy' format for simple manipulation with the tidyverse summarise function but it is in a nice format for presentation.  My question is how to reproduce this output in R?</p>
  <pre class="lang-py prettyprint-override"><code>import pandas as pd
  mtcars_df = pd.read_csv(filepath_or_buffer=&quot;data/mtcars.csv&quot;)
  
  mtcars_df.describe()
  '''
               mpg        cyl        disp  ...         am       gear     carb
  count  32.000000  32.000000   32.000000  ...  32.000000  32.000000  32.0000
  mean   20.090625   6.187500  230.721875  ...   0.406250   3.687500   2.8125
  std     6.026948   1.785922  123.938694  ...   0.498991   0.737804   1.6152
  min    10.400000   4.000000   71.100000  ...   0.000000   3.000000   1.0000
  25%    15.425000   4.000000  120.825000  ...   0.000000   3.000000   2.0000
  50%    19.200000   6.000000  196.300000  ...   0.000000   4.000000   2.0000
  75%    22.800000   8.000000  326.000000  ...   1.000000   4.000000   4.0000
  max    33.900000   8.000000  472.000000  ...   1.000000   5.000000   8.0000
  '''
  </code></pre>
  <p>To reproduce this output in R I have used the base R summary function.  Unfortunately the output duplicated the stat labels on every column.  To remove the labels I wrangled the table into a dataframe and stripped out the labels with regex!  Far more effort than I was expecting.  If there is a cleaner, easier way in R I would love to know.</p>
  <pre class="lang-r prettyprint-override"><code>library(tidyverse)
  library(rebus)
  #&gt; 
  #&gt; Attaching package: 'rebus'
  #&gt; The following object is masked from 'package:stringr':
  #&gt; 
  #&gt;     regex
  #&gt; The following object is masked from 'package:ggplot2':
  #&gt; 
  #&gt;     alpha
      
  stats_table &lt;- summary(mtcars)
  stats_table
  #&gt;       mpg             cyl             disp             hp       
  #&gt;  Min.   :10.40   Min.   :4.000   Min.   : 71.1   Min.   : 52.0  
  #&gt;  1st Qu.:15.43   1st Qu.:4.000   1st Qu.:120.8   1st Qu.: 96.5  
  #&gt;  Median :19.20   Median :6.000   Median :196.3   Median :123.0  
  #&gt;  Mean   :20.09   Mean   :6.188   Mean   :230.7   Mean   :146.7  
  #&gt;  3rd Qu.:22.80   3rd Qu.:8.000   3rd Qu.:326.0   3rd Qu.:180.0  
  #&gt;  Max.   :33.90   Max.   :8.000   Max.   :472.0   Max.   :335.0  
  #&gt;       drat             wt             qsec             vs        
  #&gt;  Min.   :2.760   Min.   :1.513   Min.   :14.50   Min.   :0.0000  
  #&gt;  1st Qu.:3.080   1st Qu.:2.581   1st Qu.:16.89   1st Qu.:0.0000  
  #&gt;  Median :3.695   Median :3.325   Median :17.71   Median :0.0000  
  #&gt;  Mean   :3.597   Mean   :3.217   Mean   :17.85   Mean   :0.4375  
  #&gt;  3rd Qu.:3.920   3rd Qu.:3.610   3rd Qu.:18.90   3rd Qu.:1.0000  
  #&gt;  Max.   :4.930   Max.   :5.424   Max.   :22.90   Max.   :1.0000  
  #&gt;        am              gear            carb      
  #&gt;  Min.   :0.0000   Min.   :3.000   Min.   :1.000  
  #&gt;  1st Qu.:0.0000   1st Qu.:3.000   1st Qu.:2.000  
  #&gt;  Median :0.0000   Median :4.000   Median :2.000  
  #&gt;  Mean   :0.4062   Mean   :3.688   Mean   :2.812  
  #&gt;  3rd Qu.:1.0000   3rd Qu.:4.000   3rd Qu.:4.000  
  #&gt;  Max.   :1.0000   Max.   :5.000   Max.   :8.000
  
  pattern &lt;- one_or_more(DGT) %R% optional(&quot;.&quot;) %R% optional(one_or_more(DGT))
  
  get_labels &lt;- as.data.frame.matrix(stats_table)[,1]
  location &lt;- str_locate_all(pattern =':', get_labels)[[1]][1]
  strip_punct &lt;- zero_or_more(PUNCT) %R% zero_or_more(SPACE) %R% PUNCT
  
  identity &lt;- str_remove_all(str_sub(string = get_labels, start = 1, end = location), strip_punct)
  
  stats_df &lt;- as.data.frame.matrix(stats_table) %&gt;%
      mutate(across(everything(), ~str_match(., pattern))) %&gt;%
      mutate(identity = identity) %&gt;%
      relocate(identity)
  
  stats_df
  #&gt;     identity      mpg      cyl      disp       hp      drat       wt      qsec
  #&gt; X        Min     10.4      4.0      71.1     52.0       2.7      1.5      14.5
  #&gt; X.1   1st Qu        1        1         1        1         1        1         1
  #&gt; X.2   Median     19.2      6.0     196.3    123.0       3.6      3.3      17.7
  #&gt; X.3     Mean     20.0      6.1     230.7    146.7       3.5      3.2      17.8
  #&gt; X.4   3rd Qu        3        3         3        3         3        3         3
  #&gt; X.5      Max     33.9      8.0     472.0    335.0       4.9      5.4      22.9
  #&gt;           vs       am      gear      carb
  #&gt; X        0.0      0.0       3.0       1.0
  #&gt; X.1        1        1         1         1
  #&gt; X.2      0.0      0.0       4.0       2.0
  #&gt; X.3      0.4      0.4       3.6       2.8
  #&gt; X.4        3        3         3         3
  #&gt; X.5      1.0      1.0       5.0       8.0
  
  </code></pre>
  <p>I can generate the same values with tidyverse and the summarise function but everything is on one row rather than being arranged with the stats for each column summarised by row.  This makes it rather difficult to read and present.</p>
  <pre class="lang-r prettyprint-override"><code>mtcars %&gt;%
      summarise_all( .funs = list(
                           min = min,
                           mean = ~ mean(., na.rm=TRUE),
                           median = median,
                           stdev = sd,
                           percentile_25 = ~ quantile(., .25)[[1]],
                           percentile_75 = ~ quantile(., .75)[[1]],
                           max = max)
                       ) %&gt;% glimpse()
  #&gt; Rows: 1
  #&gt; Columns: 77
  #&gt; $ mpg_min            &lt;dbl&gt; 10.4
  #&gt; $ cyl_min            &lt;dbl&gt; 4
  #&gt; $ disp_min           &lt;dbl&gt; 71.1
  #&gt; $ hp_min             &lt;dbl&gt; 52
  #&gt; $ drat_min           &lt;dbl&gt; 2.76
  #&gt; $ wt_min             &lt;dbl&gt; 1.513
  #&gt; $ qsec_min           &lt;dbl&gt; 14.5
  #&gt; $ vs_min             &lt;dbl&gt; 0
  #&gt; $ am_min             &lt;dbl&gt; 0
  #&gt; $ gear_min           &lt;dbl&gt; 3
  #&gt; $ carb_min           &lt;dbl&gt; 1
  #&gt; $ mpg_mean           &lt;dbl&gt; 20.09062
  #&gt; $ cyl_mean           &lt;dbl&gt; 6.1875
  #&gt; $ disp_mean          &lt;dbl&gt; 230.7219
  #&gt; $ hp_mean            &lt;dbl&gt; 146.6875
  #&gt; $ drat_mean          &lt;dbl&gt; 3.596563
  #&gt; $ wt_mean            &lt;dbl&gt; 3.21725
  #&gt; $ qsec_mean          &lt;dbl&gt; 17.84875
  #&gt; $ vs_mean            &lt;dbl&gt; 0.4375
  #&gt; $ am_mean            &lt;dbl&gt; 0.40625
  #&gt; $ gear_mean          &lt;dbl&gt; 3.6875
  #&gt; $ carb_mean          &lt;dbl&gt; 2.8125
  #&gt; $ mpg_median         &lt;dbl&gt; 19.2
  #&gt; $ cyl_median         &lt;dbl&gt; 6
  #&gt; $ disp_median        &lt;dbl&gt; 196.3
  #&gt; $ hp_median          &lt;dbl&gt; 123
  #&gt; $ drat_median        &lt;dbl&gt; 3.695
  #&gt; $ wt_median          &lt;dbl&gt; 3.325
  #&gt; $ qsec_median        &lt;dbl&gt; 17.71
  #&gt; $ vs_median          &lt;dbl&gt; 0
  #&gt; $ am_median          &lt;dbl&gt; 0
  #&gt; $ gear_median        &lt;dbl&gt; 4
  #&gt; $ carb_median        &lt;dbl&gt; 2
  #&gt; $ mpg_stdev          &lt;dbl&gt; 6.026948
  #&gt; $ cyl_stdev          &lt;dbl&gt; 1.785922
  #&gt; $ disp_stdev         &lt;dbl&gt; 123.9387
  #&gt; $ hp_stdev           &lt;dbl&gt; 68.56287
  #&gt; $ drat_stdev         &lt;dbl&gt; 0.5346787
  #&gt; $ wt_stdev           &lt;dbl&gt; 0.9784574
  #&gt; $ qsec_stdev         &lt;dbl&gt; 1.786943
  #&gt; $ vs_stdev           &lt;dbl&gt; 0.5040161
  #&gt; $ am_stdev           &lt;dbl&gt; 0.4989909
  #&gt; $ gear_stdev         &lt;dbl&gt; 0.7378041
  #&gt; $ carb_stdev         &lt;dbl&gt; 1.6152
  #&gt; $ mpg_percentile_25  &lt;dbl&gt; 15.425
  #&gt; $ cyl_percentile_25  &lt;dbl&gt; 4
  #&gt; $ disp_percentile_25 &lt;dbl&gt; 120.825
  #&gt; $ hp_percentile_25   &lt;dbl&gt; 96.5
  #&gt; $ drat_percentile_25 &lt;dbl&gt; 3.08
  #&gt; $ wt_percentile_25   &lt;dbl&gt; 2.58125
  #&gt; $ qsec_percentile_25 &lt;dbl&gt; 16.8925
  #&gt; $ vs_percentile_25   &lt;dbl&gt; 0
  #&gt; $ am_percentile_25   &lt;dbl&gt; 0
  #&gt; $ gear_percentile_25 &lt;dbl&gt; 3
  #&gt; $ carb_percentile_25 &lt;dbl&gt; 2
  #&gt; $ mpg_percentile_75  &lt;dbl&gt; 22.8
  #&gt; $ cyl_percentile_75  &lt;dbl&gt; 8
  #&gt; $ disp_percentile_75 &lt;dbl&gt; 326
  #&gt; $ hp_percentile_75   &lt;dbl&gt; 180
  #&gt; $ drat_percentile_75 &lt;dbl&gt; 3.92
  #&gt; $ wt_percentile_75   &lt;dbl&gt; 3.61
  #&gt; $ qsec_percentile_75 &lt;dbl&gt; 18.9
  #&gt; $ vs_percentile_75   &lt;dbl&gt; 1
  #&gt; $ am_percentile_75   &lt;dbl&gt; 1
  #&gt; $ gear_percentile_75 &lt;dbl&gt; 4
  #&gt; $ carb_percentile_75 &lt;dbl&gt; 4
  #&gt; $ mpg_max            &lt;dbl&gt; 33.9
  #&gt; $ cyl_max            &lt;dbl&gt; 8
  #&gt; $ disp_max           &lt;dbl&gt; 472
  #&gt; $ hp_max             &lt;dbl&gt; 335
  #&gt; $ drat_max           &lt;dbl&gt; 4.93
  #&gt; $ wt_max             &lt;dbl&gt; 5.424
  #&gt; $ qsec_max           &lt;dbl&gt; 22.9
  #&gt; $ vs_max             &lt;dbl&gt; 1
  #&gt; $ am_max             &lt;dbl&gt; 1
  #&gt; $ gear_max           &lt;dbl&gt; 5
  #&gt; $ carb_max           &lt;dbl&gt; 8
  </code></pre>
  <p><sup>Created on 2022-03-12 by the <a href="https://reprex.tidyverse.org" rel="nofollow noreferrer">reprex package</a> (v2.0.1)</sup></p>