<p>You can extract the necessary values and add them directly the plot as a second layer using <code>plot + geom_text()</code>. This is very similar to the inner workings of the <code>DimPlot</code> function with <code>label = TRUE</code> but allows you to use anything as label.</p>
<pre><code># I use dplyr v1.0.2 for all data frame manipulations 
# First the cluster annotation and the tsne embeddings are merged
label.df &lt;- cbind(as.data.frame(seurat_object@meta.data), as.data.frame(seurat_object@reductions$tsne@cell.embeddings)) 

# using dplyr across to calculate the mean mitochondrial percentage and 
# the median tsne values per cluster
label.df &lt;- label.df %&gt;% dplyr::group_by(seurat_clusters) %&gt;% dplyr::summarise(dplyr::across(percent.mito, ~ mean(.), .names = &quot;mean_{.col}&quot;), dplyr::across(contains(&quot;tSNE&quot;), ~ median(.)))

# running FeaturePlot and adding the label layer
FeaturePlot(seurat_object, reduction = &quot;tsne&quot;, features = &quot;percent.mito&quot;) + 
  geom_text(data = label.df, aes(x = tSNE_1, y = tSNE_2, label = mean_percent.mito))
</code></pre>
<p>The labels are positioned at the median position of the cells of a cluster. Typing <code>LabelClusters</code> (without brackets) in the R console will show you the labeling function used by Seurat that contains some additional ways of positioning the labels.<br />
If you want to avoid overlapping labels you can use <code>geom_text_repel</code> from the <code>ggrepel</code> package.</p>

CC BY-SA 4.0
2020-10-27T15:37:05.397Z
I think this is exactly what I need @PPK. As the code is written, I get the following error: `Error: unexpected ',' in: label.df <- label.df %>% group_by(seurat_clusters) %>% summarise(across(percent.mito, ~ mean(.), .names = "mean_{.col}")),"
Execution halted`. I believe summarise should hold both of the across items??? I have tinkered with it at bit, but have yet to resolve the error.
cer
CC BY-SA 4.0
2020-10-27T15:42:36.097Z
`label.df <- label.df %>% group_by(seurat_clusters) %>% summarise(across(percent.mito, ~ mean(.), .names = "mean_{.col}"), across(contains("    tSNE"), ~ median(.)))`  Yields this error: 
`Error: `across()` must only be used inside dplyr verbs.`  ...                     └─dplyr::across(percent.mito, ~mean(.), .names = "mean_{.col}")` -- any tips?
cer
CC BY-SA 4.0
2020-10-27T16:44:11.680Z
Sorry for the typo in the answer. As you correctly determined the summarise function was closed too early. I modified the answer accordingly.
PPK
CC BY-SA 4.0
2020-10-27T16:48:53.600Z
The second problem seems to be with some other package you are using overwriting some dplyr function. This happens pretty frequently because the functions have such generic names. I updated the answer with explicit calls to dplyr::group:by, dplyr::summarise and dplyr::across to circumvent this kind of problem. Hope this helps
PPK
CC BY-SA 4.0
2020-10-27T21:28:19.483Z
There is still some issue. I will post the error at the end of my post to preserve the format.
cer
CC BY-SA 4.0
2020-10-27T21:34:12.697Z
Is your code expecting seurat_clusters to be integers rather than characters?
cer
CC BY-SA 4.0
2020-10-28T11:47:24.400Z
The code should work for both integers and characters.
PPK
CC BY-SA 4.0
2020-10-28T11:51:51.310Z
I looked for solutions and it may be that the error is caused by the expressions `~ mean(.)` and `~ median(.)`. You could try to replace this with simply `mean` and `median`.  For me both work equally well but after reading more documentation the second version may be more correct. If this solves your problem I will update the code in the answer again
PPK
CC BY-SA 4.0
2020-10-28T13:18:23.290Z
Let us [continue this discussion in chat](https://chat.stackexchange.com/rooms/115609/discussion-between-cer-and-ppk).
cer
CC BY-SA 4.0
2020-10-26T15:27:51.103Z
2020-10-27T16:48:17.967Z
2020-10-27T16:48:17.967Z
PPK
<p>I have a tricky data set with cells that will have a higher percent of mitochondria genes than &quot;typical&quot; data sets. I would like to look at the mito percentage in each cluster without any filtering (<em>e.g.</em>, <code>subset(seurat_object, subset = nFeature_RNA &gt; 200 &amp; nFeature_RNA &lt; 3000 &amp; percent.mito &lt; 10)</code>)</p>
<pre><code>FeaturePlot(seurat_object, features=c('percent.mito'), cols = c(&quot;gold&quot;, &quot;firebrick4&quot;), pt.size=0.5, label=TRUE, repel=TRUE)
</code></pre>
<p>Done!</p>
<p>I would also like to calculate the average percent.mito for each cluster and place the value on the FeaturePlot/TSNE plots. I have no idea how to manipulate a Seurat object to calculate these values but I can probably figure out how to place those values on the graph -- probably with <code>geom_text</code>.</p>
<p>My Hail Mary approach is below:</p>
<pre><code>p3 &lt;- FeaturePlot(seurat_object, features=c('percent.mito'), cols = c(&quot;gold&quot;, &quot;firebrick4&quot;), pt.size=0.5, label=TRUE, repel=TRUE)
p3 &lt;- lapply(X = p3, FUN = function(x) mean(p3))
CombinePlots(plots = p3)
</code></pre>
<p>My attempts yield the error below; I don't know if my approach is close to correct and therefore, I have spent little time working through the error message.</p>
<pre><code>In mean.default(p3) : argument is not numeric or logical: returning NA
Execution halted
</code></pre>
<p>and</p>
<pre><code>p3 &lt;- FeaturePlot(seurat_object, features=c('percent.mito'), cols = c(&quot;gold&quot;, &quot;firebrick4&quot;), pt.size=0.5, label=TRUE, repel=TRUE)
p3 &lt;- lapply(X = p3, FUN = function(x) mean(seurat_object$meta.data$seurat_clusters))
CombinePlots(plots = p3)
</code></pre>
<pre><code>Error in `[[.Seurat`(x, i, drop = TRUE) :
  Cannot find 'meta.data' in this Seurat object
Calls: lapply ... lapply -&gt; FUN -&gt; mean -&gt; $ -&gt; $.Seurat -&gt; [[ -&gt; [[.Seurat
</code></pre>
<p>and</p>
<pre><code>p3 &lt;- FeaturePlot(seurat_object, features=c('percent.mito'), cols = c(&quot;gold&quot;, &quot;firebrick4&quot;), pt.size=0.5, label=TRUE, repel=TRUE)
p3 &lt;- lapply(X = seurat_object, FUN = function(x) mean(seurat_object$meta.data$seurat_clusters))
CombinePlots(plots = p3)
</code></pre>
<pre><code>Error in as.list.default(X) : 
  no method for coercing this S4 class to a vector
Calls: lapply -&gt; lapply -&gt; as.list -&gt; as.list.default```
</code></pre>
<p>If anyone can provide some guidance -- you will be inducted into my personal hall of fame. :)</p>
<p>Thank you for your time.</p>
<p>P.S. I am using Seurat 3.1</p>
<p>Update:</p>
<pre><code>md = surate_object@meta.data 
md_mean &lt;- ddply(md, .(seurat_clusters), summarize, mean_mito_per=mean(percent.mito))
</code></pre>
<p>This was not the approach I was hoping for but I have the values -- which when expanded to the length of the original df, I can insert it into a free slot in the seurat_object.</p>
<p>If anyone has a better approach I would love to hear it.</p>
<p>UPDATE:
Using @PPK's approach</p>
<pre><code># First the cluster annotation and the tsne embeddings are merged
label.df &lt;- cbind(as.data.frame(seurat_object@meta.data), as.data.frame(seurat_object@reductions$tsne@cell.embeddings)) 

# using dplyr across to calculate the mean mitochondrial percentage and 
# the median tsne values per cluster
label.df &lt;- label.df %&gt;% dplyr::group_by(seurat_clusters) %&gt;% dplyr::summarise(dplyr::across(percent.mito, ~ mean(.), .names = &quot;mean_{.col}&quot;), dplyr::across(contains(&quot;tSNE&quot;), ~ median(.)))

# running FeaturePlot and adding the label layer
FeaturePlot(seurat_object, reduction = &quot;tsne&quot;, features = &quot;percent.mito&quot;) + 
  geom_text(data = label.df, aes(x = tSNE_1, y = tSNE_2, label = mean_percent.mito))
</code></pre>
<p>Error:</p>
<pre><code>* object '.col' is a function.
ℹ Input `..1` is `dplyr::across(percent.mito, ~mean(.), .names = &quot;mean_{.col}&quot;)`.
ℹ The error occured in group 1: seurat_clusters = &quot;0&quot;.
Backtrace:
     █
  1. └─global::initial_clustering(BM0H, &quot;BM0H&quot;)
  2.   └─`%&gt;%`(...)
  3.     ├─base::withVisible(eval(quote(`_fseq`(`_lhs`)), env, env))
  4.     └─base::eval(quote(`_fseq`(`_lhs`)), env, env)
  5.       └─base::eval(quote(`_fseq`(`_lhs`)), env, env)
  6.         └─`_fseq`(`_lhs`)
  7.           └─magrittr::freduce(value, `_function_list`)
  8.             ├─base::withVisible(function_list[[k]](value))
  9.             └─function_list[[k]](value)
 10.               ├─dplyr::summarise(...)
 11.               └
</code></pre>

<seurat><single-cell><clustering>
Calculating average mito.percentage for each cluster (seurat)
# Calculating average mito.percentage for each cluster (seurat)

(Asked by: username_0 on 2020-10-25)

I have a tricky data set with cells that will have a higher percent of mitochondria genes than "typical" data sets. I would like to look at the mito percentage in each cluster without any filtering (e.g., subset(seurat_object, subset = nFeature_RNA > 200 & nFeature_RNA < 3000 & percent.mito < 10))
FeaturePlot(seurat_object, features=c('percent.mito'), cols = c("gold", "firebrick4"), pt.size=0.5, label=TRUE, repel=TRUE)

Done!
I would also like to calculate the average percent.mito for each cluster and place the value on the FeaturePlot/TSNE plots. I have no idea how to manipulate a Seurat object to calculate these values but I can probably figure out how to place those values on the graph -- probably with geom_text.
My Hail Mary approach is below:
p3 <- FeaturePlot(seurat_object, features=c('percent.mito'), cols = c("gold", "firebrick4"), pt.size=0.5, label=TRUE, repel=TRUE)
p3 <- lapply(X = p3, FUN = function(x) mean(p3))
CombinePlots(plots = p3)

My attempts yield the error below; I don't know if my approach is close to correct and therefore, I have spent little time working through the error message.
In mean.default(p3) : argument is not numeric or logical: returning NA
Execution halted

and
p3 <- FeaturePlot(seurat_object, features=c('percent.mito'), cols = c("gold", "firebrick4"), pt.size=0.5, label=TRUE, repel=TRUE)
p3 <- lapply(X = p3, FUN = function(x) mean(seurat_object$meta.data$seurat_clusters))
CombinePlots(plots = p3)

Error in `[[.Seurat`(x, i, drop = TRUE) :
  Cannot find 'meta.data' in this Seurat object
Calls: lapply ... lapply -> FUN -> mean -> $ -> $.Seurat -> [[ -> [[.Seurat

and
p3 <- FeaturePlot(seurat_object, features=c('percent.mito'), cols = c("gold", "firebrick4"), pt.size=0.5, label=TRUE, repel=TRUE)
p3 <- lapply(X = seurat_object, FUN = function(x) mean(seurat_object$meta.data$seurat_clusters))
CombinePlots(plots = p3)

Error in as.list.default(X) : 
  no method for coercing this S4 class to a vector
Calls: lapply -> lapply -> as.list -> as.list.default```

If anyone can provide some guidance -- you will be inducted into my personal hall of fame. :)
Thank you for your time.
P.S. I am using Seurat 3.1
Update:
md = surate_object@meta.data 
md_mean <- ddply(md, .(seurat_clusters), summarize, mean_mito_per=mean(percent.mito))

This was not the approach I was hoping for but I have the values -- which when expanded to the length of the original df, I can insert it into a free slot in the seurat_object.
If anyone has a better approach I would love to hear it.
UPDATE:
Using @PPK's approach
# First the cluster annotation and the tsne embeddings are merged
label.df <- cbind(as.data.frame(seurat_object@meta.data), as.data.frame(seurat_object@reductions$tsne@cell.embeddings)) 

# using dplyr across to calculate the mean mitochondrial percentage and 
# the median tsne values per cluster
label.df <- label.df %>% dplyr::group_by(seurat_clusters) %>% dplyr::summarise(dplyr::across(percent.mito, ~ mean(.), .names = "mean_{.col}"), dplyr::across(contains("tSNE"), ~ median(.)))

# running FeaturePlot and adding the label layer
FeaturePlot(seurat_object, reduction = "tsne", features = "percent.mito") + 
  geom_text(data = label.df, aes(x = tSNE_1, y = tSNE_2, label = mean_percent.mito))

Error:
* object '.col' is a function.
ℹ Input `..1` is `dplyr::across(percent.mito, ~mean(.), .names = "mean_{.col}")`.
ℹ The error occured in group 1: seurat_clusters = "0".
Backtrace:
     █
  1. └─global::initial_clustering(BM0H, "BM0H")
  2.   └─`%>%`(...)
  3.     ├─base::withVisible(eval(quote(`_fseq`(`_lhs`)), env, env))
  4.     └─base::eval(quote(`_fseq`(`_lhs`)), env, env)
  5.       └─base::eval(quote(`_fseq`(`_lhs`)), env, env)
  6.         └─`_fseq`(`_lhs`)
  7.           └─magrittr::freduce(value, `_function_list`)
  8.             ├─base::withVisible(function_list[[k]](value))
  9.             └─function_list[[k]](value)
 10.               ├─dplyr::summarise(...)
 11.               └

**Tags:** seurat, single-cell, clustering

---

## 1 answers

### Answer by username_1 on 2020-10-26. Score: 2 | Accepted answer

You can extract the necessary values and add them directly the plot as a second layer using plot + geom_text(). This is very similar to the inner workings of the DimPlot function with label = TRUE but allows you to use anything as label.
# I use dplyr v1.0.2 for all data frame manipulations 
# First the cluster annotation and the tsne embeddings are merged
label.df <- cbind(as.data.frame(seurat_object@meta.data), as.data.frame(seurat_object@reductions$tsne@cell.embeddings)) 

# using dplyr across to calculate the mean mitochondrial percentage and 
# the median tsne values per cluster
label.df <- label.df %>% dplyr::group_by(seurat_clusters) %>% dplyr::summarise(dplyr::across(percent.mito, ~ mean(.), .names = "mean_{.col}"), dplyr::across(contains("tSNE"), ~ median(.)))

# running FeaturePlot and adding the label layer
FeaturePlot(seurat_object, reduction = "tsne", features = "percent.mito") + 
  geom_text(data = label.df, aes(x = tSNE_1, y = tSNE_2, label = mean_percent.mito))

The labels are positioned at the median position of the cells of a cluster. Typing LabelClusters (without brackets) in the R console will show you the labeling function used by Seurat that contains some additional ways of positioning the labels.
If you want to avoid overlapping labels you can use geom_text_repel from the ggrepel package.

> **Comments:**
> **username_0**: I think this is exactly what I need @PPK. As the code is written, I get the following error: `Error: unexpected ',' in: label.df <- label.df %>% group_by(seurat_clusters) %>% summarise(across(percent.mito, ~ mean(.), .names = "mean_{.col}"))," Execution halted`. I believe summarise should hold both of the across items??? I have tinkered with it at bit, but have yet to resolve the error.  
> **username_0**: `label.df <- label.df %>% group_by(seurat_clusters) %>% summarise(across(percent.mito, ~ mean(.), .names = "mean_{.col}"), across(contains("    tSNE"), ~ median(.)))`  Yields this error:  `Error: `across()` must only be used inside dplyr verbs.`  ...                     └─dplyr::across(percent.mito, ~mean(.), .names = "mean_{.col}")` -- any tips?  
> **username_1**: Sorry for the typo in the answer. As you correctly determined the summarise function was closed too early. I modified the answer accordingly.  
> **username_1**: The second problem seems to be with some other package you are using overwriting some dplyr function. This happens pretty frequently because the functions have such generic names. I updated the answer with explicit calls to dplyr::group:by, dplyr::summarise and dplyr::across to circumvent this kind of problem. Hope this helps  
> **username_0**: There is still some issue. I will post the error at the end of my post to preserve the format.  
> **username_0**: Is your code expecting seurat_clusters to be integers rather than characters?  
> **username_1**: The code should work for both integers and characters.  
> **username_1**: I looked for solutions and it may be that the error is caused by the expressions `~ mean(.)` and `~ median(.)`. You could try to replace this with simply `mean` and `median`.  For me both work equally well but after reading more documentation the second version may be more correct. If this solves your problem I will update the code in the answer again  
> **username_0**: Let us [continue this discussion in chat](https://chat.stackexchange.com/rooms/115609/discussion-between-cer-and-ppk).  

---

