<p>As you suggest one way of solving your problem would be by selecting all atoms that don't have backbone atoms names. In a pdb file I believe backbone atoms would be named 'CA', 'HA', 'N', 'HN' or 'H', 'C' and 'O'. Beware of the N-terminal (where the hydrogens would be named either 'H1', 'H2' &amp; 'H3'; or 'HN1', 'HN2' and 'HN3') and the C-terminal (the other oxygen would be named 'OXT').</p>
<p>One way of doing it is by using the handy selections from pandas dataframes. It's probably a more elegant way than making a list of dataframes.</p>
<pre class="lang-py prettyprint-override"><code>from biopandas.pdb import PandasPdb

bpdf_1 = PandasPdb().fetch_pdb('1t48').df['ATOM']
bpdf_2 = PandasPdb().fetch_pdb('1t49').df['ATOM']

# Function returns a list of boolean. True when it's not a backbone atom.
sel = lambda df: df['atom_name'].str.contains('[^(CA|HA|N|C|O|HN|H)]$')

# The number of atom is not the same, so I only align the first 200
sidechain_rmsd = PandasPdb.rmsd(bpdf_1[sel][:200], bpdf_2[sel][:200])
</code></pre>
<p>The regex says something like &quot;(not (CA or HA or …)) end of line&quot;. The end of line is important, otherwise any atom containing N, C, O or H in their name would be excluded (so most of them).</p>
<p>Note that with this method it's still possible to use the PandasPdb selectors. For example, to only consider heavy atoms (no hydrogen):</p>
<pre class="lang-py prettyprint-override"><code>sidechain_rmsd = PandasPdb.rmsd(bpdf_1[sel][:200],
                                bpdf_2[sel][:200],
                                s='heavy')
</code></pre>
<p><strong>EDIT:</strong></p>
<p>Another way to do it would be:</p>
<pre class="lang-py prettyprint-override"><code>sidechain_rmsd = PandasPdb.rmsd(bpdf_1[:200],
                                bpdf_2[:200],
                                s='main chain',
                                invert=True)
</code></pre>
<p>You don't need any selection in this case, <strong>BUT</strong> this is a bad idea if you have hydrogens, as it will also align them (including the backbone ones).</p>

CC BY-SA 4.0
2022-03-01T12:53:21.173Z
If you want to align each residue one by one instead: I'm sure this gives you the tools to do it, though it would be a pretty bad idea. In this very case I'd advise to try comparing side-chains using dihedral angles (hence rotamers). You can have a look at pyrosetta for this.
O.Laprevote
CC BY-SA 4.0
2022-03-01T13:40:42.610Z
Thanks for your detailed response! To check that I understood right: the H[0-9]* and HN[0-9]* are the names of the hydrogen atoms in the amino group? So I need to filter them out.
CubeHead
CC BY-SA 4.0
2022-03-01T15:55:18.770Z
Yes, although you can get away by just filtering out H[1-3] and HN[1-3]. More hydrogen atoms would be disturbing.
O.Laprevote
CC BY-SA 4.0
2022-03-01T16:19:49.890Z
My bad: H[1-3]? and HN[1-3]?, otherwise you don't cover cases where there is only H or only HN. Remember to keep the $ at the end, otherwise you'd end up matching all H.
O.Laprevote
CC BY-SA 4.0
2022-03-01T20:49:54.353Z
Thanks for your help!
CubeHead
CC BY-SA 4.0
2022-03-01T12:18:35.537Z
2022-03-01T12:44:55.907Z
2022-03-01T12:44:55.907Z
O.Laprevote
<p>I am currently implementing some metrics I could use for comparing two conformations of the same protein in Python.</p>
<p>For example, I know I could use the RMSD of all protein ATOMS in BioPandas using this:</p>
<pre><code>biopandas.pdb.PandasPdb.rmsd(conformation1.df['ATOM'], conformation2.df['ATOM'])
</code></pre>
<p>Are there some out-of-the-box tools in Python for measuring the RMSD between the side chains of the two conformations? When trying to do this manually, the main issue is how to identify side chain atoms, which is not clear for me. I know how to get all atoms from a residue using BioPandas:</p>
<pre><code>aminoacids = list(PandasPdb().read_pdb(my_pdb_filename).df['ATOM'].groupby(['chain_id', 'residue_number', 'residue_name']))
</code></pre>
<p>Now <code>aminoacids[i]</code> is a dataframe containing all the atoms of the i-th aminoacid, like this:</p>
<p><a href="https://i.sstatic.net/9RnK2.png" rel="nofollow noreferrer"><img src="https://i.sstatic.net/9RnK2.png" alt="enter image description here" /></a></p>
<p>From here, it's not clear how to separate back bone atoms from side chain atoms. It's clear that the one with atom_number 'CA' will be the alpha-Carbon, but I am not sure how to identify the others. But once the separation is done, the side chain RMSD could be measure for example using <code>Bio.SVDSuperimposer.SVDSuperimposer</code> which works directly on the array of 3D coordinates of the atoms.</p>
<p>Thanks!</p>

<python><proteins><protein-structure>
Identify side chain atoms in BioPandas dataframe
# Identify side chain atoms in BioPandas dataframe

(Asked by: username_0 on 2022-02-28)

I am currently implementing some metrics I could use for comparing two conformations of the same protein in Python.
For example, I know I could use the RMSD of all protein ATOMS in BioPandas using this:
biopandas.pdb.PandasPdb.rmsd(conformation1.df['ATOM'], conformation2.df['ATOM'])

Are there some out-of-the-box tools in Python for measuring the RMSD between the side chains of the two conformations? When trying to do this manually, the main issue is how to identify side chain atoms, which is not clear for me. I know how to get all atoms from a residue using BioPandas:
aminoacids = list(PandasPdb().read_pdb(my_pdb_filename).df['ATOM'].groupby(['chain_id', 'residue_number', 'residue_name']))

Now aminoacids[i] is a dataframe containing all the atoms of the i-th aminoacid, like this:

From here, it's not clear how to separate back bone atoms from side chain atoms. It's clear that the one with atom_number 'CA' will be the alpha-Carbon, but I am not sure how to identify the others. But once the separation is done, the side chain RMSD could be measure for example using Bio.SVDSuperimposer.SVDSuperimposer which works directly on the array of 3D coordinates of the atoms.
Thanks!

**Tags:** python, proteins, protein-structure

---

## 1 answers

### Answer by username_1 on 2022-03-01. Score: 3 | Accepted answer

As you suggest one way of solving your problem would be by selecting all atoms that don't have backbone atoms names. In a pdb file I believe backbone atoms would be named 'CA', 'HA', 'N', 'HN' or 'H', 'C' and 'O'. Beware of the N-terminal (where the hydrogens would be named either 'H1', 'H2' & 'H3'; or 'HN1', 'HN2' and 'HN3') and the C-terminal (the other oxygen would be named 'OXT').
One way of doing it is by using the handy selections from pandas dataframes. It's probably a more elegant way than making a list of dataframes.
from biopandas.pdb import PandasPdb

bpdf_1 = PandasPdb().fetch_pdb('1t48').df['ATOM']
bpdf_2 = PandasPdb().fetch_pdb('1t49').df['ATOM']

# Function returns a list of boolean. True when it's not a backbone atom.
sel = lambda df: df['atom_name'].str.contains('[^(CA|HA|N|C|O|HN|H)]$')

# The number of atom is not the same, so I only align the first 200
sidechain_rmsd = PandasPdb.rmsd(bpdf_1[sel][:200], bpdf_2[sel][:200])

The regex says something like "(not (CA or HA or …)) end of line". The end of line is important, otherwise any atom containing N, C, O or H in their name would be excluded (so most of them).
Note that with this method it's still possible to use the PandasPdb selectors. For example, to only consider heavy atoms (no hydrogen):
sidechain_rmsd = PandasPdb.rmsd(bpdf_1[sel][:200],
                                bpdf_2[sel][:200],
                                s='heavy')

EDIT:
Another way to do it would be:
sidechain_rmsd = PandasPdb.rmsd(bpdf_1[:200],
                                bpdf_2[:200],
                                s='main chain',
                                invert=True)

You don't need any selection in this case, BUT this is a bad idea if you have hydrogens, as it will also align them (including the backbone ones).

> **Comments:**
> **username_1**: If you want to align each residue one by one instead: I'm sure this gives you the tools to do it, though it would be a pretty bad idea. In this very case I'd advise to try comparing side-chains using dihedral angles (hence rotamers). You can have a look at pyrosetta for this.  
> **username_0**: Thanks for your detailed response! To check that I understood right: the H[0-9]* and HN[0-9]* are the names of the hydrogen atoms in the amino group? So I need to filter them out.  
> **username_1**: Yes, although you can get away by just filtering out H[1-3] and HN[1-3]. More hydrogen atoms would be disturbing.  
> **username_1**: My bad: H[1-3]? and HN[1-3]?, otherwise you don't cover cases where there is only H or only HN. Remember to keep the $ at the end, otherwise you'd end up matching all H.  
> **username_0**: Thanks for your help!  

---

