{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "BioPandas\n", "\n", "Author: Sebastian Raschka \n", "License: BSD 3 clause \n", "Project Website: http://rasbt.github.io/biopandas/ \n", "Code Repository: https://github.com/rasbt/biopandas " ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Last updated: 2022-05-12\n", "\n", "pandas : 1.4.0\n", "biopandas: 0.4.0\n", "\n" ] } ], "source": [ "%load_ext watermark\n", "%watermark -d -u -p pandas,biopandas" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "from biopandas.pdb import PandasPdb\n", "import pandas as pd\n", "pd.set_option('display.width', 600)\n", "pd.set_option('display.max_columns', 8)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Working with PDB Structures in DataFrames" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Loading PDB Files" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "There are several ways to load a PDB structure into a `PandasPdb` object.\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 1 -- Loading a PDB file from the Protein Data Bank\n", "\n", "PDB files can be directly fetched from The Protein Data Bank at [http://www.rcsb.org](http://www.rcsb.org) via its unique 4-letter after initializing a new [`PandasPdb`](../api/biopandas.pdb#pandaspdb) object and calling the [`fetch_pdb`](../api/biopandas.pdb#pandaspdbfetch_pdb) method:" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "from biopandas.pdb import PandasPdb\n", "\n", "# Initialize a new PandasPdb object\n", "# and fetch the PDB file from rcsb.org\n", "ppdb = PandasPdb().fetch_pdb('3eiy')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 2 -- Loading a PDB file from the AlphaFold Structure Database" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "(*New in version 0.4.0*)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "PDB files can be directly fetched from The AlphaFold Structure Database at [https://alphafold.ebi.ac.uk/](https://alphafold.ebi.ac.uk/) via its unique [UniProt](https://www.uniprot.org/) Identifier after initializing a new [`PandasPdb`](../api/biopandas.pdb#pandaspdb) object and calling the [`fetch_af2`](../api/biopandas.pdb#pandaspdbfetch_pdb) method:" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "from biopandas.pdb import PandasPdb\n", "\n", "# Initialize a new PandasPdb object\n", "# and fetch the PDB file from alphafold.ebi.ac.uk\n", "ppdb = PandasPdb().fetch_pdb(uniprot_id='Q5VSL9', source=\"alphafold2-v2\")" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
record_nameatom_numberblank_1atom_name...segment_idelement_symbolchargeline_idx
0ATOM1N...NNaN111
1ATOM2CA...CNaN112
2ATOM3C...CNaN113
3ATOM4CB...CNaN114
4ATOM5O...ONaN115
..............................
6713ATOM6714CG...CNaN6824
6714ATOM6715CD...CNaN6825
6715ATOM6716NE2...NNaN6826
6716ATOM6717OE1...ONaN6827
6717ATOM6718OXT...ONaN6828
\n", "

6718 rows × 21 columns

\n", "
" ], "text/plain": [ " record_name atom_number blank_1 atom_name ... segment_id element_symbol charge line_idx\n", "0 ATOM 1 N ... N NaN 111\n", "1 ATOM 2 CA ... C NaN 112\n", "2 ATOM 3 C ... C NaN 113\n", "3 ATOM 4 CB ... C NaN 114\n", "4 ATOM 5 O ... O NaN 115\n", "... ... ... ... ... ... ... ... ... ...\n", "6713 ATOM 6714 CG ... C NaN 6824\n", "6714 ATOM 6715 CD ... C NaN 6825\n", "6715 ATOM 6716 NE2 ... N NaN 6826\n", "6716 ATOM 6717 OE1 ... O NaN 6827\n", "6717 ATOM 6718 OXT ... O NaN 6828\n", "\n", "[6718 rows x 21 columns]" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ppdb.df[\"ATOM\"]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 3 a) -- Loading a PDB structure from a local file\n", "\n", "\n", "Alternatively, we can load PDB files from local directories as regular PDB files using [`read_pdb`](../api/biopandas.pdb#pandaspdbread_pdb):" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ppdb.read_pdb('./data/3eiy.pdb')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "[File link: [3eiy.pdb](https://raw.githubusercontent.com/rasbt/biopandas/main/docs/tutorials/data/3eiy.pdb)]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 3 b) -- Loading a PDB structure from a local gzipped PDB file\n", "\n", "Or, we can load them from gzip archives like so (note that the file must end with a '.gz' suffix in order to be recognized as a gzip file):" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ppdb.read_pdb('./data/3eiy.pdb.gz')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "[File link: [3eiy.pdb.gz](https://github.com/rasbt/biopandas/blob/main/docs/tutorials/data/3eiy.pdb.gz?raw=true)]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "After the file was succesfully loaded, we have access to the following attributes:" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "PDB Code: 3eiy\n", "PDB Header Line: HYDROLASE 17-SEP-08 3EIY\n", "\n", "Raw PDB file contents:\n", "\n", "HEADER HYDROLASE 17-SEP-08 3EIY \n", "TITLE CRYSTAL STRUCTURE OF INORGANIC PYROPHOSPHATASE FROM BURKHOLDERIA \n", "TITLE 2 PSEUDOMALLEI WITH BOUND PYROPHOSPHATE \n", "COMPND MOL_ID: 1; \n", "COMPND 2 MOLECULE: INORGANIC PYROPHOSPHATASE; \n", "COMPND 3 CHAIN: A; \n", "COMPND 4 EC: 3.6.1.1; \n", "COMPND 5 ENGINEERED: YES \n", "SOURCE MOL_ID: 1; \n", "SOURCE 2 ORGANISM_SCIENTIFIC: BURKHOLDERIA PSEUDOMALLEI 1710B; \n", "SOURCE 3 ORGANISM_TAXID: 320372; \n", "SOURCE 4 GENE: PPA, BURPS1710B_1237; \n", "SOURCE 5 EXPRESSION_SYSTEM\n", "...\n" ] } ], "source": [ "print('PDB Code: %s' % ppdb.code)\n", "print('PDB Header Line: %s' % ppdb.header)\n", "print('\\nRaw PDB file contents:\\n\\n%s\\n...' % ppdb.pdb_text[:1000])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The most interesting / useful attribute is the [`PandasPdb.df`](../api/biopandas.pdb#pandaspdbdf) DataFrame dictionary though, which gives us access to the PDB files as pandas DataFrames. Let's print the first 3 lines from the `ATOM` coordinate section to see how it looks like:" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
record_nameatom_numberblank_1atom_name...segment_idelement_symbolchargeline_idx
0ATOM1N...NNaN609
1ATOM2CA...CNaN610
2ATOM3C...CNaN611
\n", "

3 rows × 21 columns

\n", "
" ], "text/plain": [ " record_name atom_number blank_1 atom_name ... segment_id element_symbol charge line_idx\n", "0 ATOM 1 N ... N NaN 609\n", "1 ATOM 2 CA ... C NaN 610\n", "2 ATOM 3 C ... C NaN 611\n", "\n", "[3 rows x 21 columns]" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ppdb.df['ATOM'].head(3)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "But more on that in the next section." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 4 -- Loading a PDB file from a Python list" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Since biopandas 0.3.0, PDB files can also be loaded into a PandasPdb object from a Python list:" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
record_nameatom_numberblank_1atom_name...segment_idelement_symbolchargeline_idx
0ATOM1N...NNaN609
1ATOM2CA...CNaN610
2ATOM3C...CNaN611
3ATOM4O...ONaN612
4ATOM5CB...CNaN613
\n", "

5 rows × 21 columns

\n", "
" ], "text/plain": [ " record_name atom_number blank_1 atom_name ... segment_id element_symbol charge line_idx\n", "0 ATOM 1 N ... N NaN 609\n", "1 ATOM 2 CA ... C NaN 610\n", "2 ATOM 3 C ... C NaN 611\n", "3 ATOM 4 O ... O NaN 612\n", "4 ATOM 5 CB ... C NaN 613\n", "\n", "[5 rows x 21 columns]" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "with open('./data/3eiy.pdb', 'r') as f:\n", " three_eiy = f.readlines()\n", "\n", "ppdb2 = PandasPdb()\n", "ppdb2.read_pdb_from_list(three_eiy)\n", "\n", "ppdb2.df['ATOM'].head()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 5 -- Obtaining a PDB file from a mmCIF structure" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Since v0.5.0, it is now also possible to obtain a `PandasPdb` object from a mmCIF file, using `PandasMmcift`'s `PandasMmcif.get_pandas_pdb()`:" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Type: \n" ] }, { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
record_nameatom_numberblank_1atom_namealt_locresidue_nameblank_2chain_idresidue_numberinsertion...x_coordy_coordz_coordoccupancyb_factorblank_4segment_idelement_symbolchargeline_idx
0ATOM1NSERA2...2.52754.656-1.6671.052.73NNaN0
1ATOM2CASERA2...3.25954.783-0.3681.052.54CNaN1
2ATOM3CSERA2...4.12753.553-0.1051.052.03CNaN2
3ATOM4OSERA2...5.27453.451-0.5941.052.45ONaN3
4ATOM5CBSERA2...2.27354.9440.7921.052.69CNaN4
\n", "

5 rows × 21 columns

\n", "
" ], "text/plain": [ " record_name atom_number blank_1 atom_name alt_loc residue_name blank_2 \\\n", "0 ATOM 1 N SER \n", "1 ATOM 2 CA SER \n", "2 ATOM 3 C SER \n", "3 ATOM 4 O SER \n", "4 ATOM 5 CB SER \n", "\n", " chain_id residue_number insertion ... x_coord y_coord z_coord \\\n", "0 A 2 ... 2.527 54.656 -1.667 \n", "1 A 2 ... 3.259 54.783 -0.368 \n", "2 A 2 ... 4.127 53.553 -0.105 \n", "3 A 2 ... 5.274 53.451 -0.594 \n", "4 A 2 ... 2.273 54.944 0.792 \n", "\n", " occupancy b_factor blank_4 segment_id element_symbol charge line_idx \n", "0 1.0 52.73 N NaN 0 \n", "1 1.0 52.54 C NaN 1 \n", "2 1.0 52.03 C NaN 2 \n", "3 1.0 52.45 O NaN 3 \n", "4 1.0 52.69 C NaN 4 \n", "\n", "[5 rows x 21 columns]" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from biopandas.mmcif import PandasMmcif\n", "\n", "\n", "mmcif = PandasMmcif().fetch_mmcif(\"3EIY\")\n", "pdb = mmcif.convert_to_pandas_pdb()\n", "\n", "print(\"Type:\", type(pdb))\n", "pdb.df[\"ATOM\"].head() " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Looking at PDBs in DataFrames" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "PDB files are parsed according to the [PDB file format description](http://www.rcsb.org/pdb/static.do?p=file_formats/pdb/index.html). More specifically, BioPandas reads the columns of the ATOM and HETATM sections as shown in the following excerpt from [http://deposit.rcsb.org/adit/docs/pdb_atom_format.html#ATOM](http://deposit.rcsb.org/adit/docs/pdb_atom_format.html#ATOM)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "| COLUMNS | DATA TYPE | CONTENTS | biopandas column name |\n", "|---------|--------------|--------------------------------------------|-----------------------|\n", "| 1 - 6 | Record name | \"ATOM\" | record_name |\n", "| 7 - 11 | Integer | Atom serial number. | atom_number |\n", "| 12 | | | blank_1 |\n", "| 13 - 16 | Atom | Atom name. | atom_name |\n", "| 17 | Character | Alternate location indicator. | alt_loc |\n", "| 18 - 20 | Residue name | Residue name. | residue_name |\n", "| 21 | | | blank_2 |\n", "| 22 | Character | Chain identifier. | chain_id |\n", "| 23 - 26 | Integer | Residue sequence number. | residue_number |\n", "| 27 | AChar | Code for insertion of residues. | insertion |\n", "| 28 - 30 | | | blank_3 |\n", "| 31 - 38 | Real(8.3) | Orthogonal coordinates for X in Angstroms. | x_coord |\n", "| 39 - 46 | Real(8.3) | Orthogonal coordinates for Y in Angstroms. | y_coord |\n", "| 47 - 54 | Real(8.3) | Orthogonal coordinates for Z in Angstroms. | z_coord |\n", "| 55 - 60 | Real(6.2) | Occupancy. | occupancy |\n", "| 61 - 66 | Real(6.2) | Temperature factor (Default = 0.0). | bfactor |\n", "| 67-72 | | | blank_4 |\n", "| 73 - 76 | LString(4) | Segment identifier, left-justified. | segment_id |\n", "| 77 - 78 | LString(2) | Element symbol, right-justified. | element_symbol |\n", "| 79 - 80 | LString(2) | Charge on the atom. | charge |" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Below is an example of how this would look like in an actual PDB file:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " Example: \n", " 1 2 3 4 5 6 7 8\n", " 12345678901234567890123456789012345678901234567890123456789012345678901234567890\n", " ATOM 145 N VAL A 25 32.433 16.336 57.540 1.00 11.92 A1 N\n", " ATOM 146 CA VAL A 25 31.132 16.439 58.160 1.00 11.85 A1 C\n", " ATOM 147 C VAL A 25 30.447 15.105 58.363 1.00 12.34 A1 C\n", " ATOM 148 O VAL A 25 29.520 15.059 59.174 1.00 15.65 A1 O\n", " ATOM 149 CB AVAL A 25 30.385 17.437 57.230 0.28 13.88 A1 C\n", " ATOM 150 CB BVAL A 25 30.166 17.399 57.373 0.72 15.41 A1 C\n", " ATOM 151 CG1AVAL A 25 28.870 17.401 57.336 0.28 12.64 A1 C\n", " ATOM 152 CG1BVAL A 25 30.805 18.788 57.449 0.72 15.11 A1 C\n", " ATOM 153 CG2AVAL A 25 30.835 18.826 57.661 0.28 13.58 A1 C\n", " ATOM 154 CG2BVAL A 25 29.909 16.996 55.922 0.72 13.25 A1 C" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "After loading a PDB file from rcsb.org or our local drive, the [`PandasPdb.df`](../api/biopandas.pdb/#pandaspdbdf) attribute should contain the following 4 DataFrame objects:" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "dict_keys(['ATOM', 'HETATM', 'ANISOU', 'OTHERS'])" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from biopandas.pdb import PandasPdb\n", "ppdb = PandasPdb()\n", "ppdb.read_pdb('./data/3eiy.pdb')\n", "ppdb.df.keys()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "[File link: [3eiy.pdb](https://raw.githubusercontent.com/rasbt/biopandas/main/docs/tutorials/data/3eiy.pdb)]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- 'ATOM': contains the entries from the ATOM coordinate section\n", "- 'HETATM': ... entries from the \"HETATM\" coordinate section \n", "- 'ANISOU': ... entries from the \"ANISOU\" coordinate section \n", "- 'OTHERS': Everything else that is *not* a 'ATOM', 'HETATM', or 'ANISOU' entry" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "![](./img/df_dict.jpg)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The columns of the 'HETATM' DataFrame are indentical to the 'ATOM' DataFrame that we've seen earlier:" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
record_nameatom_numberblank_1atom_name...segment_idelement_symbolchargeline_idx
0HETATM1332K...KNaN1940
1HETATM1333NA...NANaN1941
\n", "

2 rows × 21 columns

\n", "
" ], "text/plain": [ " record_name atom_number blank_1 atom_name ... segment_id element_symbol charge line_idx\n", "0 HETATM 1332 K ... K NaN 1940\n", "1 HETATM 1333 NA ... NA NaN 1941\n", "\n", "[2 rows x 21 columns]" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ppdb.df['HETATM'].head(2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note that \"ANISOU\" entries are handled a bit differently as specified at [http://deposit.rcsb.org/adit/docs/pdb_atom_format.html#ATOM](http://deposit.rcsb.org/adit/docs/pdb_atom_format.html#ATOM)." ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
record_nameatom_numberblank_1atom_name...blank_4element_symbolchargeline_idx
\n", "

0 rows × 21 columns

\n", "
" ], "text/plain": [ "Empty DataFrame\n", "Columns: [record_name, atom_number, blank_1, atom_name, alt_loc, residue_name, blank_2, chain_id, residue_number, insertion, blank_3, U(1,1), U(2,2), U(3,3), U(1,2), U(1,3), U(2,3), blank_4, element_symbol, charge, line_idx]\n", "Index: []\n", "\n", "[0 rows x 21 columns]" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ppdb.df['ANISOU'].head(2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Not every PDB file contains ANISOU entries (similarly, some PDB files may only contain HETATM or ATOM entries). If records are basent, the DataFrame will be empty as show above." ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ppdb.df['ANISOU'].empty" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Since the DataFrames are fairly wide, let's us take a look at the columns by accessing the DataFrame's `column` attribute:" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Index(['record_name', 'atom_number', 'blank_1', 'atom_name', 'alt_loc', 'residue_name', 'blank_2', 'chain_id', 'residue_number', 'insertion', 'blank_3', 'U(1,1)', 'U(2,2)', 'U(3,3)', 'U(1,2)', 'U(1,3)', 'U(2,3)', 'blank_4', 'element_symbol', 'charge', 'line_idx'], dtype='object')" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ppdb.df['ANISOU'].columns" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "ANISOU records are very similar to ATOM/HETATM records. In fact, the columns 7 - 27 and 73 - 80 are identical to their corresponding ATOM/HETATM records, which means that the 'ANISOU' DataFrame doesn't have the following entries:" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{'b_factor', 'occupancy', 'segment_id', 'x_coord', 'y_coord', 'z_coord'}" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "set(ppdb.df['ATOM'].columns).difference(set(ppdb.df['ANISOU'].columns))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Instead, the \"ANISOU\" DataFrame contains the anisotropic temperature factors \"U(-,-)\" -- note that these are scaled by a factor of $10^4$ ($\\text{Angstroms}^2$) by convention." ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{'U(1,1)', 'U(1,2)', 'U(1,3)', 'U(2,2)', 'U(2,3)', 'U(3,3)'}" ] }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" } ], "source": [ "set(ppdb.df['ANISOU'].columns).difference(set(ppdb.df['ATOM'].columns))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Ah, another interesting thing to mention is that the columns already come with the types you'd expect (where `object` essentially \"means\" `str` here):" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "record_name object\n", "atom_number int64\n", "blank_1 object\n", "atom_name object\n", "alt_loc object\n", "residue_name object\n", "blank_2 object\n", "chain_id object\n", "residue_number int64\n", "insertion object\n", "blank_3 object\n", "x_coord float64\n", "y_coord float64\n", "z_coord float64\n", "occupancy float64\n", "b_factor float64\n", "blank_4 object\n", "segment_id object\n", "element_symbol object\n", "charge float64\n", "line_idx int64\n", "dtype: object" ] }, "execution_count": 18, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ppdb.df['ATOM'].dtypes" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Typically, all good things come in threes, however, there is a 4th DataFrame, an'OTHER' DataFrame, which contains everything that wasn't parsed as 'ATOM', 'HETATM', or 'ANISOU' coordinate section:" ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
record_nameentryline_idx
0HEADERHYDROLASE 17...0
1TITLECRYSTAL STRUCTURE OF INORGANIC PYROPHOSPHA...1
2TITLE2 PSEUDOMALLEI WITH BOUND PYROPHOSPHATE2
3COMPNDMOL_ID: 1;3
4COMPND2 MOLECULE: INORGANIC PYROPHOSPHATASE;4
\n", "
" ], "text/plain": [ " record_name entry line_idx\n", "0 HEADER HYDROLASE 17... 0\n", "1 TITLE CRYSTAL STRUCTURE OF INORGANIC PYROPHOSPHA... 1\n", "2 TITLE 2 PSEUDOMALLEI WITH BOUND PYROPHOSPHATE 2\n", "3 COMPND MOL_ID: 1; 3\n", "4 COMPND 2 MOLECULE: INORGANIC PYROPHOSPHATASE; 4" ] }, "execution_count": 19, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ppdb.df['OTHERS'].head(5)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Although these 'OTHER' entries are typically less useful for structure-related computations, you may still want to take a look at them to get a short summary of the PDB structure and learn about it's potential quirks and gotchas (typically listed in the REMARKs section). Lastly, the \"OTHERS\" DataFrame comes in handy if we want to reconstruct the structure as PDB file as we will see later (note the `line_idx` columns in all of the DataFrames)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Working with PDB DataFrames" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In the previous sections, we've seen how to load PDB structures into DataFrames, and how to access them. Now, let's talk about manipulating PDB files in DataFrames." ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
record_nameatom_numberblank_1atom_name...segment_idelement_symbolchargeline_idx
0ATOM1N...NNaN609
1ATOM2CA...CNaN610
2ATOM3C...CNaN611
3ATOM4O...ONaN612
4ATOM5CB...CNaN613
\n", "

5 rows × 21 columns

\n", "
" ], "text/plain": [ " record_name atom_number blank_1 atom_name ... segment_id element_symbol charge line_idx\n", "0 ATOM 1 N ... N NaN 609\n", "1 ATOM 2 CA ... C NaN 610\n", "2 ATOM 3 C ... C NaN 611\n", "3 ATOM 4 O ... O NaN 612\n", "4 ATOM 5 CB ... C NaN 613\n", "\n", "[5 rows x 21 columns]" ] }, "execution_count": 20, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from biopandas.pdb import PandasPdb\n", "ppdb = PandasPdb()\n", "ppdb.read_pdb('./data/3eiy.pdb.gz')\n", "ppdb.df['ATOM'].head()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "[File link: [3eiy.pdb.gz](https://github.com/rasbt/biopandas/blob/main/docs/tutorials/data/3eiy.pdb.gz?raw=true)]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Okay, there's actually not *that* much to say ... \n", "Once we have our PDB file in the DataFrame format, we have the whole convenience of [pandas](http://pandas.pydata.org) right there at our fingertips." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "For example, let's get all Proline residues:" ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
record_nameatom_numberblank_1atom_name...segment_idelement_symbolchargeline_idx
38ATOM39N...NNaN647
39ATOM40CA...CNaN648
40ATOM41C...CNaN649
41ATOM42O...ONaN650
42ATOM43CB...CNaN651
\n", "

5 rows × 21 columns

\n", "
" ], "text/plain": [ " record_name atom_number blank_1 atom_name ... segment_id element_symbol charge line_idx\n", "38 ATOM 39 N ... N NaN 647\n", "39 ATOM 40 CA ... C NaN 648\n", "40 ATOM 41 C ... C NaN 649\n", "41 ATOM 42 O ... O NaN 650\n", "42 ATOM 43 CB ... C NaN 651\n", "\n", "[5 rows x 21 columns]" ] }, "execution_count": 21, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ppdb.df['ATOM'][ppdb.df['ATOM']['residue_name'] == 'PRO'].head()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Or main chain atoms:" ] }, { "cell_type": "code", "execution_count": 22, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
record_nameatom_numberblank_1atom_name...segment_idelement_symbolchargeline_idx
2ATOM3C...CNaN611
8ATOM9C...CNaN617
19ATOM20C...CNaN628
25ATOM26C...CNaN634
33ATOM34C...CNaN642
\n", "

5 rows × 21 columns

\n", "
" ], "text/plain": [ " record_name atom_number blank_1 atom_name ... segment_id element_symbol charge line_idx\n", "2 ATOM 3 C ... C NaN 611\n", "8 ATOM 9 C ... C NaN 617\n", "19 ATOM 20 C ... C NaN 628\n", "25 ATOM 26 C ... C NaN 634\n", "33 ATOM 34 C ... C NaN 642\n", "\n", "[5 rows x 21 columns]" ] }, "execution_count": 22, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ppdb.df['ATOM'][ppdb.df['ATOM']['atom_name'] == 'C'].head()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It's also easy to strip our coordinate section from hydrogen atoms if there are any ..." ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
record_nameatom_numberblank_1atom_name...segment_idelement_symbolchargeline_idx
0ATOM1N...NNaN609
1ATOM2CA...CNaN610
2ATOM3C...CNaN611
3ATOM4O...ONaN612
4ATOM5CB...CNaN613
\n", "

5 rows × 21 columns

\n", "
" ], "text/plain": [ " record_name atom_number blank_1 atom_name ... segment_id element_symbol charge line_idx\n", "0 ATOM 1 N ... N NaN 609\n", "1 ATOM 2 CA ... C NaN 610\n", "2 ATOM 3 C ... C NaN 611\n", "3 ATOM 4 O ... O NaN 612\n", "4 ATOM 5 CB ... C NaN 613\n", "\n", "[5 rows x 21 columns]" ] }, "execution_count": 23, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ppdb.df['ATOM'][ppdb.df['ATOM']['element_symbol'] != 'H'].head()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Or, let's compute the average temperature factor of our protein main chain:" ] }, { "cell_type": "code", "execution_count": 24, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Average B-Factor [Main Chain]: 28.83\n" ] } ], "source": [ "mainchain = ppdb.df['ATOM'][(ppdb.df['ATOM']['atom_name'] == 'C') | \n", " (ppdb.df['ATOM']['atom_name'] == 'O') | \n", " (ppdb.df['ATOM']['atom_name'] == 'N') | \n", " (ppdb.df['ATOM']['atom_name'] == 'CA')]\n", "\n", "bfact_mc_avg = mainchain['b_factor'].mean()\n", "print('Average B-Factor [Main Chain]: %.2f' % bfact_mc_avg)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Loading PDB files from a Python List**" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Since biopandas 0.3.0, PDB files can also be loaded into a PandasPdb object from a Python list:" ] }, { "cell_type": "code", "execution_count": 25, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
record_nameatom_numberblank_1atom_name...segment_idelement_symbolchargeline_idx
0ATOM1N...NNaN609
1ATOM2CA...CNaN610
2ATOM3C...CNaN611
3ATOM4O...ONaN612
4ATOM5CB...CNaN613
\n", "

5 rows × 21 columns

\n", "
" ], "text/plain": [ " record_name atom_number blank_1 atom_name ... segment_id element_symbol charge line_idx\n", "0 ATOM 1 N ... N NaN 609\n", "1 ATOM 2 CA ... C NaN 610\n", "2 ATOM 3 C ... C NaN 611\n", "3 ATOM 4 O ... O NaN 612\n", "4 ATOM 5 CB ... C NaN 613\n", "\n", "[5 rows x 21 columns]" ] }, "execution_count": 25, "metadata": {}, "output_type": "execute_result" } ], "source": [ "with open('./data/3eiy.pdb', 'r') as f:\n", " three_eiy = f.readlines()\n", "\n", "ppdb2 = PandasPdb()\n", "ppdb2.read_pdb_from_list(three_eiy)\n", "\n", "ppdb2.df['ATOM'].head()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Working with PDBs Containing Multiple Models\n", "\n", "(*New in version 0.4.0*)\n", "\n", "Some PDB files, particularly those containing NMR structures, provide an ensemble of models. There are various ways to extract these.\n", "\n", "In these examples we will work with [2JYF](https://www.rcsb.org/structure/2JYF): an RNA structure containing 10 models of the same underlying RNA structure.\n", "\n", "To start, we con obtain a DataFrame denoting the lines of the PDB files corresponding to each model." ] }, { "cell_type": "code", "execution_count": 26, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "/Users/sebastian/Desktop/biopandas/biopandas/pdb/pandas_pdb.py:680: SettingWithCopyWarning: \n", "A value is trying to be set on a copy of a slice from a DataFrame.\n", "Try using .loc[row_indexer,col_indexer] = value instead\n", "\n", "See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy\n", " idxs[\"end_idx\"] = ends.line_idx.values\n" ] }, { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
record_namemodel_idxstart_idxend_idx
129MODEL11292896
133MODEL228975664
137MODEL356658432
141MODEL4843311200
145MODEL51120113968
149MODEL61396916736
153MODEL71673719504
157MODEL81950522272
161MODEL92227325040
165MODEL102504127808
\n", "
" ], "text/plain": [ " record_name model_idx start_idx end_idx\n", "129 MODEL 1 129 2896\n", "133 MODEL 2 2897 5664\n", "137 MODEL 3 5665 8432\n", "141 MODEL 4 8433 11200\n", "145 MODEL 5 11201 13968\n", "149 MODEL 6 13969 16736\n", "153 MODEL 7 16737 19504\n", "157 MODEL 8 19505 22272\n", "161 MODEL 9 22273 25040\n", "165 MODEL 10 25041 27808" ] }, "execution_count": 26, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from biopandas.pdb import PandasPdb\n", "\n", "ppdb = PandasPdb().read_pdb('./data/2jyf.pdb')\n", "ppdb.get_model_start_end()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Assigning model IDs to the PDB DataFrames**\n", "\n", "For ease of use, the `label_models()` method adds an additional column, `\"model_id\"` to the dataframes contained within the `PandasPdb` object." ] }, { "cell_type": "code", "execution_count": 27, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "/Users/sebastian/Desktop/biopandas/biopandas/pdb/pandas_pdb.py:680: SettingWithCopyWarning: \n", "A value is trying to be set on a copy of a slice from a DataFrame.\n", "Try using .loc[row_indexer,col_indexer] = value instead\n", "\n", "See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy\n", " idxs[\"end_idx\"] = ends.line_idx.values\n" ] }, { "data": { "text/plain": [ "0 1\n", "1 1\n", "2 1\n", "3 1\n", "4 1\n", " ..\n", "27635 10\n", "27636 10\n", "27637 10\n", "27638 10\n", "27639 10\n", "Name: model_id, Length: 27640, dtype: int64" ] }, "execution_count": 27, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from biopandas.pdb import PandasPdb\n", "ppdb = PandasPdb().read_pdb('./data/2jyf.pdb')\n", "\n", "ppdb.label_models()\n", "ppdb.df[\"ATOM\"][\"model_id\"]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Subsetting `PandasPdb` objects to a given model**\n", "\n", "We can obtain new `PandasPdb` objects containing only a given model using the `get_model()` method" ] }, { "cell_type": "code", "execution_count": 28, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "/Users/sebastian/Desktop/biopandas/biopandas/pdb/pandas_pdb.py:680: SettingWithCopyWarning: \n", "A value is trying to be set on a copy of a slice from a DataFrame.\n", "Try using .loc[row_indexer,col_indexer] = value instead\n", "\n", "See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy\n", " idxs[\"end_idx\"] = ends.line_idx.values\n" ] }, { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
record_nameatom_numberblank_1atom_name...element_symbolchargeline_idxmodel_id
8292ATOM1O5'...ONaN84344
8293ATOM2C5'...CNaN84354
8294ATOM3C4'...CNaN84364
8295ATOM4O4'...ONaN84374
8296ATOM5C3'...CNaN84384
..............................
11051ATOM2761HO2'...HNaN111944
11052ATOM2762H1'...HNaN111954
11053ATOM2763H3...HNaN111964
11054ATOM2764H5...HNaN111974
11055ATOM2765H6...HNaN111984
\n", "

2764 rows × 22 columns

\n", "
" ], "text/plain": [ " record_name atom_number blank_1 atom_name ... element_symbol charge line_idx model_id\n", "8292 ATOM 1 O5' ... O NaN 8434 4\n", "8293 ATOM 2 C5' ... C NaN 8435 4\n", "8294 ATOM 3 C4' ... C NaN 8436 4\n", "8295 ATOM 4 O4' ... O NaN 8437 4\n", "8296 ATOM 5 C3' ... C NaN 8438 4\n", "... ... ... ... ... ... ... ... ... ...\n", "11051 ATOM 2761 HO2' ... H NaN 11194 4\n", "11052 ATOM 2762 H1' ... H NaN 11195 4\n", "11053 ATOM 2763 H3 ... H NaN 11196 4\n", "11054 ATOM 2764 H5 ... H NaN 11197 4\n", "11055 ATOM 2765 H6 ... H NaN 11198 4\n", "\n", "[2764 rows x 22 columns]" ] }, "execution_count": 28, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from biopandas.pdb import PandasPdb\n", "ppdb = PandasPdb().read_pdb('./data/2jyf.pdb')\n", "\n", "model_4 = ppdb.get_model(model_index=4)\n", "model_4.df[\"ATOM\"]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Subsetting `PandasPdb` objects to a list of given models**\n", "\n", "We can obtain new `PandasPdb` objects containing only a given models using the `get_models()` method" ] }, { "cell_type": "code", "execution_count": 29, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "/Users/sebastian/Desktop/biopandas/biopandas/pdb/pandas_pdb.py:680: SettingWithCopyWarning: \n", "A value is trying to be set on a copy of a slice from a DataFrame.\n", "Try using .loc[row_indexer,col_indexer] = value instead\n", "\n", "See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy\n", " idxs[\"end_idx\"] = ends.line_idx.values\n" ] }, { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
record_nameatom_numberblank_1atom_name...element_symbolchargeline_idxmodel_id
2764ATOM1O5'...ONaN28982
2765ATOM2C5'...CNaN28992
2766ATOM3C4'...CNaN29002
2767ATOM4O4'...ONaN29012
2768ATOM5C3'...CNaN29022
..............................
22107ATOM2761HO2'...HNaN222668
22108ATOM2762H1'...HNaN222678
22109ATOM2763H3...HNaN222688
22110ATOM2764H5...HNaN222698
22111ATOM2765H6...HNaN222708
\n", "

11056 rows × 22 columns

\n", "
" ], "text/plain": [ " record_name atom_number blank_1 atom_name ... element_symbol charge line_idx model_id\n", "2764 ATOM 1 O5' ... O NaN 2898 2\n", "2765 ATOM 2 C5' ... C NaN 2899 2\n", "2766 ATOM 3 C4' ... C NaN 2900 2\n", "2767 ATOM 4 O4' ... O NaN 2901 2\n", "2768 ATOM 5 C3' ... C NaN 2902 2\n", "... ... ... ... ... ... ... ... ... ...\n", "22107 ATOM 2761 HO2' ... H NaN 22266 8\n", "22108 ATOM 2762 H1' ... H NaN 22267 8\n", "22109 ATOM 2763 H3 ... H NaN 22268 8\n", "22110 ATOM 2764 H5 ... H NaN 22269 8\n", "22111 ATOM 2765 H6 ... H NaN 22270 8\n", "\n", "[11056 rows x 22 columns]" ] }, "execution_count": 29, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from biopandas.pdb import PandasPdb\n", "ppdb = PandasPdb().read_pdb('./data/2jyf.pdb')\n", "\n", "model_ensemble = ppdb.get_models(model_indices=[2, 4, 6, 8])\n", "model_ensemble.df[\"ATOM\"]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Plotting" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Since we are using pandas under the hood, which in turns uses matplotlib under the hood, we can produce quick summary plots of our PDB structures relatively conveniently:" ] }, { "cell_type": "code", "execution_count": 30, "metadata": {}, "outputs": [], "source": [ "from biopandas.pdb import PandasPdb\n", "ppdb = PandasPdb().read_pdb('./data/3eiy.pdb.gz')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "[File link: [3eiy.pdb.gz](https://github.com/rasbt/biopandas/blob/main/docs/tutorials/data/3eiy.pdb.gz?raw=true)]" ] }, { "cell_type": "code", "execution_count": 31, "metadata": {}, "outputs": [], "source": [ "%matplotlib inline\n", "import matplotlib.pyplot as plt\n", "from matplotlib import style\n", "style.use('ggplot')" ] }, { "cell_type": "code", "execution_count": 32, "metadata": {}, "outputs": [ { "data": { "image/png": "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", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "ppdb.df['ATOM']['b_factor'].plot(kind='hist')\n", "plt.title('Distribution of B-Factors')\n", "plt.xlabel('B-factor')\n", "plt.ylabel('count')\n", "plt.show()" ] }, { "cell_type": "code", "execution_count": 33, "metadata": {}, "outputs": [ { "data": { "image/png": "iVBORw0KGgoAAAANSUhEUgAAAYUAAAEaCAYAAAD+E0veAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjUuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/YYfK9AAAACXBIWXMAAAsTAAALEwEAmpwYAABxGUlEQVR4nO2dd5gTVffHv3eS3WyvWcoC0ot0FFRUWBSwoqI/CyoCiqggomAByws2dBERRJCiAhZeXxEFxC6CWLAsIFKlyVKkLNvYXpK5vz8mM5mZzCSTniz38zz7bDL1zGTmnnvKPZdQSikYDAaDwQDAhVsABoPBYEQOTCkwGAwGQ4IpBQaDwWBIMKXAYDAYDAmmFBgMBoMhwZQCg8FgMCSYUmCEnQEDBuDee+8NtxhuefbZZ9GuXbtwi+EThBB88MEH4RbDMK1atcKLL77odptRo0Zh0KBBfp8rPz8fhBD8/PPPEXGcSIApBT8ZNWoUCCHSX2pqKvr27Ysvv/zS477PPvusYl/xLzc312+5fv75ZxBCkJ+f7/ex/OXkyZOIi4tDkyZNUF9fH25x3HLs2DEQQvDDDz+E/NxbtmyByWTCeeedF9DjnjhxAjfffHNAj+mJbt26wWQyYfv27V7vm5eXh4kTJwZEjl9//RU33XQTGjdujLi4OLRt2xbDhw/H1q1bA3J8kRYtWuDEiRO48MILA3rccMCUQgDo168fTpw4gRMnTuC3337Deeedh6FDh+LgwYMe923VqpW0r/j30EMPhUBq49TV1fm1/5IlS3DttdciMzMTa9asCZBUDY9FixZh7NixyM/Px+bNmwN23CZNmiAuLi5gx/PEpk2bUFBQgNGjR2Px4sVe75+VlYXExES/5Vi6dCn69euHmJgYLF++HHv27MFHH32EVq1a4eGHH/b7+HJMJhOaNGmCmJiYgB43LFCGX4wcOZIOHDhQsaysrIwCoJ9++qnbfadNm0bbtm2ruW758uX0ggsuoCkpKTQzM5Nec801dO/evYptTp06RUeNGkUbNWpELRYL7dChA33nnXfooUOHKADFX05ODqWUUp7n6cyZM2nr1q1pTEwMbdOmDZ09e7biuC1btqRPP/00HTt2LM3IyKC9e/emlFL61ltv0U6dOlGLxUIzMjJov3796NGjR91eo91up61ataJr1qyhM2bMoIMHD3bZJicnh44ePVr6XldXRydPnkyzs7NpTEwMPffcc+ny5csV+wCg8+fPp8OHD6dJSUm0efPmdMaMGYptCgsL6c0330wTEhJoo0aN6DPPPENHjBjh8nupjyv/a9myJaXU+VutXr2aduzYkSYkJNABAwbQAwcOKPbfvHkzHTx4ME1MTKRWq5XeeOONND8/3+09olR4ZpKSkuhff/1Fx44dS8eMGaMp29y5c+mtt95KExISaIsWLejHH39MS0tL6R133EGTkpJo69at6cqVK132e//99726d2VlZfS+++6jVquVWiwWev7559NvvvnG43VQSumIESPoxIkT6e+//05TU1NpZWWlYn19fT197rnnaJs2bWhsbCzNzs6m48ePl9a3bNmSvvDCC9L34uJi6ZobNWpEn376aY+/47///kstFgu9//77NdcXFxdTSqn0rnz00Ud0yJAhND4+nrZu3Zq+9957iu3nzJlDe/ToQRMTE2njxo3pbbfdRo8fPy6tF4/z008/eXXcSIQpBT9RK4Xa2lo6a9YsarFYPDYG7pTCkiVL6Nq1a+mBAwfo1q1b6XXXXUfbtWtHa2trKaWUVlVV0U6dOtFevXrR7777jh48eJB+88039MMPP6Q2m42uWbOGAqB//PEHPXHiBC0qKqKUUjpv3jwaFxdHFy1aRPft20cXLFhALRYLffvtt6Vzt2zZkiYnJ9Np06bRvXv30l27dtHNmzdTk8lE3333XZqfn0+3b99O33rrLY9K4auvvqJZWVm0vr6eHj9+nMbExNCDBw8qtlErhccee4xmZGTQFStW0L1799Lp06dTQghdt26dtA0A2qhRI7p48WJ64MAB+vrrr1MAdP369dI21113HW3fvj1dv3493blzJx01ahRNSUlx25hs3bqVAqCffPIJPXHiBC0oKJB+q4SEBHrllVfSzZs3023bttGePXvS/v37S/vu2rWLJiYm0qlTp9I9e/bQ7du305tvvpm2b9+eVldXu71PCxYsoL169aKUUvr777/TpKQkWl5ertgGAG3cuDFdtmwZ3b9/Px07diyNj4+nV111FV26dCndv38/HT9+PE1ISKCFhYWK/dRKwdO9u/nmm2nLli3p119/TXfv3k0nTJhAY2Ji6J49e9xeR3FxMY2Pj6fbtm2jlFLauXNnunTpUsU2I0aMoFlZWfS9996jBw4coL/++it97bXXpPVqpTB06FDatm1b+v3339OdO3fSO++8kyYnJ7v9HWfPnk0BeHw+xca7devW9KOPPqL79++nkydPpiaTie7bt0/abs6cOfS7776j//zzD920aRPt27ev4rfXUwqejhuJMKXgJyNHjqQmk4kmJibSxMRESgihiYmJ9KOPPvK477Rp06Tt5X81NTUu2xYVFVEA9Oeff6aUUvr2229Ti8Wi+9D/9NNPFAA9dOiQYnnz5s3p448/rlj2yCOP0NatW0vfW7ZsSS+//HLFNp9++ilNSUmhZ86c8XhdcoYOHUofeeQR6fvVV19Nn3zyScU2cqVQWVlJY2Nj6fz5812Oc9lll0nfAdCHHnpIsU3Hjh3plClTKKWU7tu3jwJQKJK6ujravHlzt43J0aNHKQC6YcMGxfJp06ZRk8kkKQlKKf3www8pIURq8EeOHElvu+02xX41NTU0Pj6erlq1SveclFLaq1cvOmfOHOl7586d6aJFixTbAKAPP/yw9L2goIACUPSyi4uLKQC6du1axX5qpeDu3u3fv58CoF988YWLjHfffbfb65gzZw7t2bOn9H3GjBm0b9++0nfx2B9//LHuMeRKQdz+22+/ldbX1tbS7Oxst7/j2LFjaUpKiltZKXU23rNmzZKW1dfX08TERLpw4ULd/cTOw7FjxxTHUSsFb48bCbCYQgC48MILsW3bNmzbtg1bt27F1KlTMXLkSHzzzTcAgOXLlyMpKUn6W758ubRvixYtpH3Fv9jYWGzbtg033ngjWrdujeTkZJxzzjkAgMOHDwMQgpKdO3dG8+bNDctZVlaGY8eOoX///orlOTk5yM/PR1VVlbTsggsuUGwzePBgtGnTBq1bt8awYcOwePFiFBYWuj3fiRMn8Pnnn2PkyJHSslGjRmHp0qWw2Wya+xw4cAB1dXWaMu7atUuxrGfPnorvzZo1w6lTpwAAu3fvBgBcdNFF0vqYmBj07t3brczuyM7ORlZWluJ8lFIUFBQAEAKkq1atUvzWmZmZqKmpwf79+3WP+8cff2DHjh244447pGUjR47U9Mf36NFD+pyVlQWTyYTu3btLy9LT0xEbGyvJpIeRe6f+Dfr37+/yG6hZvHix4ve+66678Mcff2Dnzp0AIAV4r7jiCrfHERFlufjii6VlsbGx6NOnj9v9qJd1PuX3w2w2o3HjxtL9AIAffvgBV155JVq0aIHk5GRceumlAJzvo6/HjUTM4RagIRAfH69IV+zZsye+//57TJ8+HVdeeSWuv/56RVZC48aNpc8xMTEuqY5VVVW44oorcOmll2LJkiVo0qQJAKBLly6KoC8hxCd51ftpvUDqQF9SUhI2b96MX375BevWrcPChQvxxBNP4Pvvv8f555+veZ533nkHNpvNpSG22+347LPPcNNNN3klo3pZbGysyz48z7s9jj9onQ+AdE6e53HXXXdhypQpLvtmZmbqHnfx4sWw2Wxo2rSptIxSCp7nsXXrVkU2klYgU71M6z4YuRZP+2j9BnJ+/vln7N69G48++igee+wxabndbsfixYsxd+5ct8fXO6cvdOzYUeoEGek4ubsfR44cwTXXXIO77roLU6dOhdVqxbFjxzBo0CCPSRi+3OdwwyyFIGE2m6Wed3JyMtq1ayf9JScnu913z549OH36NKZPn47LLrsM5557LkpKShQvyPnnn49du3bh2LFjmscQH0a73S4tS0lJQfPmzbFx40bFtj/++CNat26NhIQEt3KZTCb0798fzz//PLZs2YKmTZviv//9r+a2PM/j7bffxlNPPeViCQ0fPlw3K6Vdu3awWCyaMnbp0sWtfHI6d+4MQEhJFLHZbNiyZYvb/bTum1F69+6N7du3o23btorfu127dkhPT9fcp6ysDP/73/8wf/58xT3666+/cNlll/mUveMv4n3+8ccfFct/+uknt7/BokWLMHjwYPz111+Ka3n99dfx/vvvo7q6WlJw3377rVeybNq0SVpWV1eHvLw8t/vdcsstsFgsuuMdSkpKDJ0fECzA6upqzJkzB5dccgk6duwY8b19f2CWQgCoq6vDyZMnAQCVlZX45ptv8M033+C5557z6XgtW7aExWLBG2+8gUcffRT5+fmYMmWKopd2++2345VXXsH111+PV155BW3btsU///yDwsJC3HbbbWjZsiU4jsOXX36J2267DRaLBampqXjyySfx6KOPon379hgwYADWr1+PBQsWYP78+W5lWrNmDf755x/0798fWVlZ2LJlC44ePSo1vmq+/vprHDlyBPfff7/k+hK5++67MXjwYOTn56NVq1aKdQkJCZgwYQL+85//ICsrCz179sTHH3+MNWvW4LvvvjN8D9u3b4/rrrsODz74IBYtWoSsrCzMmjULZWVlbnu7VqsV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", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "ppdb.df['ATOM']['b_factor'].plot(kind='line')\n", "plt.title('B-Factors Along the Amino Acid Chain')\n", "plt.xlabel('Residue Number')\n", "plt.ylabel('B-factor in $A^2$')\n", "plt.show()" ] }, { "cell_type": "code", "execution_count": 34, "metadata": {}, "outputs": [ { "data": { "image/png": "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FGnaaDSKXr16tejTsmXLRHBwsDhz5ow4e/asCAsLE5s3b26xrzVr1oioqCghxP9/MDh27JhXdZL3eA2CAADi599sbOuumunTp+PVV1/F4MGDMWvWLGzZssXrtgcNGuTVehkZGdLfarUagwYNQmlpqdf78db+/ftx3333tZhnNBohhGixvz59+iAkJESa7tGjB06ePNlmu83bXt72fffdh/37919zncuWLcOYMWMQFBQEAHj00Udx9uxZbN68+Yrb7d+/H3379m1xDal79+648847W9ShUCiQkpIiTTdfDxkwYECLebW1tWhqarrm+rt3745HH30Uy5cvl+ratm0bJkyY0GK9QYMGQaVSSdNDhw6F2+3GkSNHsH//fpw7dw7/9E//hIiICOn1zDPPoK6uDjU1NRgwYAB++ctfon///vjNb36DRYsWoby8/JrrpdYYEAQA2LdvHxQKBRITE2WX5+Tk4Mcff8SkSZPgcDjwwAMPYOzYsV61HR4efl01iUt+aLj5gval85qamuDxeK6r7baC8NL5wcHBrZaJ6/jxYyHENd/Oevz4cXzxxRd48803oVaroVarER4eDpfL5dXFarn9XV6HUqls8cbcvKw5kC6ddz39BoBJkybBbDajpqYGy5cvR3p6OlJTU6+4zaX7av73Xbt2LXbt2iW99u7di8OHD0Or1UKlUmHz5s2wWCxIT0/HJ598gjvuuAMbN268rprp/zEgCPX19Xj77beRlZUFnU7X5nq/+MUvkJOTg/feew8rV67EBx98gPr6egAX30yv51PmpS69rbaxsRF2ux19+vQBAMTExAAAKisrpXV27drV4s2k+Q39anX069cPVqu1xTyr1QqFQoG+ffted/39+vUDgFZHV9988420zFvLly9Hnz59sHv37hZvjGvXrsWmTZuk23blxr1fv37Yv38/Tp06Jc07efIkDh06dM113KgRI0YgISEBf/rTn/D++++3OnoAALvd3qIPf/vb3xAcHIykpCT069cPoaGhOHr0KAwGQ6tXc8ApFAoMGjQIs2fPxpYtW2A0GrF69Wq/9fNWxYDoZNxuN6qqquBwOFBaWopVq1Zh0KBBuHDhAt5+++02t3v22WexadMm6bD/008/RXx8PLp27QoAuP3227F161YcP34cp06duq5P9gUFBdi0aRMOHDiAyZMn4+TJk5g8eTKA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", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "ppdb.df['ATOM']['element_symbol'].value_counts().plot(kind='bar')\n", "plt.title('Distribution of Atom Types')\n", "plt.xlabel('elements')\n", "plt.ylabel('count')\n", "plt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Computing the Root Mean Square Deviation" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "BioPandas also comes with certain convenience functions, for example, ..." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The Root-mean-square deviation (RMSD) is simply a measure of the average distance between atoms of 2 protein or ligand structures. This calculation of the Cartesian error follows the equation:\n", "\n", "$$\n", "RMSD(a, b) = \\sqrt{\\frac{1}{n} \\sum^{n}_{i=1} \\big((a_{ix})^2 + (a_{iy})^2 + (a_{iz})^2 \\big)}\n", "= \\sqrt{\\frac{1}{n} \\sum^{n}_{i=1} || a_i + b_i||_2^2}\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "So, assuming that the we have the following 2 conformations of a ligand molecule\n", "\n", "![](./img/ligand_rmsd.png)\n", "\n", "we can compute the RMSD as follows:" ] }, { "cell_type": "code", "execution_count": 35, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "RMSD: 2.6444 Angstrom\n" ] } ], "source": [ "from biopandas.pdb import PandasPdb\n", "\n", "l_1 = PandasPdb().read_pdb('./data/lig_conf_1.pdb')\n", "l_2 = PandasPdb().read_pdb('./data/lig_conf_2.pdb')\n", "r = PandasPdb.rmsd(l_1.df['HETATM'], l_2.df['HETATM'],\n", " s=None) # all atoms, including hydrogens\n", "print('RMSD: %.4f Angstrom' % r)" ] }, { "cell_type": "code", "execution_count": 36, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
record_nameatom_numberblank_1atom_name...segment_idelement_symbolchargeline_idx
0HETATM1C1...CNaN0
1HETATM2O1...ONaN1
2HETATM3C2...CNaN2
3HETATM4O2...ONaN3
4HETATM5C3...CNaN4
5HETATM6O3...ONaN5
6HETATM7C4...CNaN6
7HETATM8O4...ONaN7
8HETATM9C5...CNaN8
9HETATM10O5...ONaN9
10HETATM11C6...CNaN10
11HETATM12O6...ONaN11
12HETATM13C7...CNaN12
13HETATM14C8...CNaN13
14HETATM15C9...CNaN14
15HETATM16C10...CNaN15
16HETATM17H1...HNaN16
17HETATM18H2...HNaN17
18HETATM19H3...HNaN18
19HETATM20H4...HNaN19
20HETATM21H5...HNaN20
21HETATM22H6...HNaN21
22HETATM23H7...HNaN22
23HETATM24H8...HNaN23
\n", "

24 rows × 21 columns

\n", "
" ], "text/plain": [ " record_name atom_number blank_1 atom_name ... segment_id element_symbol charge line_idx\n", "0 HETATM 1 C1 ... C NaN 0\n", "1 HETATM 2 O1 ... O NaN 1\n", "2 HETATM 3 C2 ... C NaN 2\n", "3 HETATM 4 O2 ... O NaN 3\n", "4 HETATM 5 C3 ... C NaN 4\n", "5 HETATM 6 O3 ... O NaN 5\n", "6 HETATM 7 C4 ... C NaN 6\n", "7 HETATM 8 O4 ... O NaN 7\n", "8 HETATM 9 C5 ... C NaN 8\n", "9 HETATM 10 O5 ... O NaN 9\n", "10 HETATM 11 C6 ... C NaN 10\n", "11 HETATM 12 O6 ... O NaN 11\n", "12 HETATM 13 C7 ... C NaN 12\n", "13 HETATM 14 C8 ... C NaN 13\n", "14 HETATM 15 C9 ... C NaN 14\n", "15 HETATM 16 C10 ... C NaN 15\n", "16 HETATM 17 H1 ... H NaN 16\n", "17 HETATM 18 H2 ... H NaN 17\n", "18 HETATM 19 H3 ... H NaN 18\n", "19 HETATM 20 H4 ... H NaN 19\n", "20 HETATM 21 H5 ... H NaN 20\n", "21 HETATM 22 H6 ... H NaN 21\n", "22 HETATM 23 H7 ... H NaN 22\n", "23 HETATM 24 H8 ... H NaN 23\n", "\n", "[24 rows x 21 columns]" ] }, "execution_count": 36, "metadata": {}, "output_type": "execute_result" } ], "source": [ "l_1.df['HETATM']" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "[File links: [lig_conf_1.pdb](https://raw.githubusercontent.com/rasbt/biopandas/master/docs/sources/tutorials/data/lig_conf_1.pdb), [lig_conf_2.pdb](https://raw.githubusercontent.com/rasbt/biopandas/master/docs/sources/tutorials/data/lig_conf_2.pdb)]" ] }, { "cell_type": "code", "execution_count": 37, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "RMSD: 1.7249 Angstrom\n" ] } ], "source": [ "r = PandasPdb.rmsd(l_1.df['HETATM'], l_2.df['HETATM'], \n", " s='carbon') # carbon atoms only\n", "print('RMSD: %.4f Angstrom' % r)" ] }, { "cell_type": "code", "execution_count": 38, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "RMSD: 1.9959 Angstrom\n" ] } ], "source": [ "r = PandasPdb.rmsd(l_1.df['HETATM'], l_2.df['HETATM'], \n", " s='heavy') # heavy atoms only\n", "print('RMSD: %.4f Angstrom' % r)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Similarly, we can compute the RMSD between 2 related protein structures:\n", "\n", "![](./img/1t48_rmsd.png)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The hydrogen-free RMSD:" ] }, { "cell_type": "code", "execution_count": 39, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "RMSD: 0.7377 Angstrom\n" ] } ], "source": [ "p_1 = PandasPdb().read_pdb('./data/1t48_995.pdb')\n", "p_2 = PandasPdb().read_pdb('./data/1t49_995.pdb')\n", "r = PandasPdb.rmsd(p_1.df['ATOM'], p_2.df['ATOM'], s='heavy')\n", "print('RMSD: %.4f Angstrom' % r)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Or the RMSD between the main chains only:" ] }, { "cell_type": "code", "execution_count": 40, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "RMSD: 0.4781 Angstrom\n" ] } ], "source": [ "p_1 = PandasPdb().read_pdb('./data/1t48_995.pdb')\n", "p_2 = PandasPdb().read_pdb('./data/1t49_995.pdb')\n", "r = PandasPdb.rmsd(p_1.df['ATOM'], p_2.df['ATOM'], s='main chain')\n", "print('RMSD: %.4f Angstrom' % r)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Filtering PDBs by Distance" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can use the `distance` method to compute the distance between each atom (or a subset of atoms) in our data frame and a three-dimensional reference point. For example:" ] }, { "cell_type": "code", "execution_count": 41, "metadata": {}, "outputs": [], "source": [ "p_1 = PandasPdb().read_pdb('./data/3eiy.pdb')\n", "\n", "reference_point = (9.362, 41.410, 10.542)\n", "distances = p_1.distance(xyz=reference_point, records=('ATOM',))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "[File link: [3eiy.pdb](https://raw.githubusercontent.com/rasbt/biopandas/main/docs/tutorials/data/3eiy.pdb)]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The distance method returns a Pandas Series object:" ] }, { "cell_type": "code", "execution_count": 42, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "0 19.267419\n", "1 18.306060\n", "2 16.976934\n", "3 16.902897\n", "4 18.124171\n", "dtype: float64" ] }, "execution_count": 42, "metadata": {}, "output_type": "execute_result" } ], "source": [ "distances.head()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "And we can use this `Series` object, for instance, to select certain atoms in our DataFrame that fall within a desired distance threshold. For example, let's select all atoms that are within 7A of our reference point: " ] }, { "cell_type": "code", "execution_count": 43, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
record_nameatom_numberblank_1atom_name...segment_idelement_symbolchargeline_idx
786ATOM787CB...CNaN1395
787ATOM788CG...CNaN1396
788ATOM789CD1...CNaN1397
789ATOM790CD2...CNaN1398
790ATOM791N...NNaN1399
\n", "

5 rows × 21 columns

\n", "
" ], "text/plain": [ " record_name atom_number blank_1 atom_name ... segment_id element_symbol charge line_idx\n", "786 ATOM 787 CB ... C NaN 1395\n", "787 ATOM 788 CG ... C NaN 1396\n", "788 ATOM 789 CD1 ... C NaN 1397\n", "789 ATOM 790 CD2 ... C NaN 1398\n", "790 ATOM 791 N ... N NaN 1399\n", "\n", "[5 rows x 21 columns]" ] }, "execution_count": 43, "metadata": {}, "output_type": "execute_result" } ], "source": [ "all_within_7A = p_1.df['ATOM'][distances < 7.0]\n", "all_within_7A.tail()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Visualized in PyMOL, this subset (yellow surface) would look as follows:\n", " \n", "![](./img/3eiy_7a.png)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Converting Amino Acid codes from 3- to 1-letter codes" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Residues in the `residue_name` field can be converted into 1-letter amino acid codes, which may be useful for further sequence analysis, for example, pair-wise or multiple sequence alignments:" ] }, { "cell_type": "code", "execution_count": 44, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
chain_idresidue_name
1378BI
1386BN
1394BY
1406BR
1417BT
\n", "
" ], "text/plain": [ " chain_id residue_name\n", "1378 B I\n", "1386 B N\n", "1394 B Y\n", "1406 B R\n", "1417 B T" ] }, "execution_count": 44, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from biopandas.pdb import PandasPdb\n", "ppdb = PandasPdb().fetch_pdb('5mtn')\n", "sequence = ppdb.amino3to1()\n", "sequence.tail()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "As shown above, the `amino3to1` method returns a `DataFrame` containing the `chain_id` and `residue_name` of the translated 1-letter amino acids. If you like to work with the sequence as a Python list of string characters, you could do the following:" ] }, { "cell_type": "code", "execution_count": 45, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['V', 'R', 'H', 'Y', 'T']" ] }, "execution_count": 45, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sequence_list = list(sequence.loc[sequence['chain_id'] == 'A', 'residue_name'])\n", "sequence_list[-5:] # last 5 residues of chain A" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "And if you prefer to work with the sequence as a string, you can use the `join` method: " ] }, { "cell_type": "code", "execution_count": 46, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'SLEPEPWFFKNLSRKDAERQLLAPGNTHGSFLIRESESTAGSFSLSVRDFDQGEVVKHYKIRNLDNGGFYISPRITFPGLHELVRHYT'" ] }, "execution_count": 46, "metadata": {}, "output_type": "execute_result" } ], "source": [ "''.join(sequence.loc[sequence['chain_id'] == 'A', 'residue_name'])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To iterate over the sequences of multi-chain proteins, you can use the `unique` method as shown below:" ] }, { "cell_type": "code", "execution_count": 47, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "Chain ID: A\n", "SLEPEPWFFKNLSRKDAERQLLAPGNTHGSFLIRESESTAGSFSLSVRDFDQGEVVKHYKIRNLDNGGFYISPRITFPGLHELVRHYT\n", "\n", "Chain ID: B\n", "SVSSVPTKLEVVAATPTSLLISWDAPAVTVVYYLITYGETGSPWPGGQAFEVPGSKSTATISGLKPGVDYTITVYAHRSSYGYSENPISINYRT\n" ] } ], "source": [ "for chain_id in sequence['chain_id'].unique():\n", " print('\\nChain ID: %s' % chain_id)\n", " print(''.join(sequence.loc[sequence['chain_id'] == chain_id, 'residue_name']))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Wrapping it up - Saving PDB structures" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Finally, let's talk about how to get the PDB structures out of the DataFrame format back into the beloved .pdb format." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's say we loaded a PDB structure, removed it from its hydrogens:" ] }, { "cell_type": "code", "execution_count": 48, "metadata": {}, "outputs": [], "source": [ "from biopandas.pdb import PandasPdb\n", "ppdb = PandasPdb().read_pdb('./data/3eiy.pdb.gz')\n", "ppdb.df['ATOM'] = ppdb.df['ATOM'][ppdb.df['ATOM']['element_symbol'] != 'H']" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "[File link: [3eiy.pdb.gz](https://github.com/rasbt/biopandas/blob/main/docs/tutorials/data/3eiy.pdb.gz?raw=true)]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can save the file using the [`PandasPdb.to_pdb`](../api/biopandas.pdb#pandaspdbto_pdb) method:" ] }, { "cell_type": "code", "execution_count": 49, "metadata": {}, "outputs": [], "source": [ "ppdb.to_pdb(path='./data/3eiy_stripped.pdb', \n", " records=None, \n", " gz=False, \n", " append_newline=True)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "[File link: [3eiy_stripped.pdb](https://raw.githubusercontent.com/rasbt/biopandas/main/docs/tutorials/data/3eiy_stripped.pdb)]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "By default, all records (that is, 'ATOM', 'HETATM', 'OTHERS', 'ANISOU') are written if we set `records=None`. Alternatively, let's say we want to get rid of the 'ANISOU' entries and produce a compressed gzip archive of our PDB structure:" ] }, { "cell_type": "code", "execution_count": 50, "metadata": {}, "outputs": [], "source": [ "ppdb.to_pdb(path='./data/3eiy_stripped.pdb.gz', \n", " records=['ATOM', 'HETATM', 'OTHERS'], \n", " gz=True, \n", " append_newline=True)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "[File link: [3eiy_stripped.pdb.gz](https://github.com/rasbt/biopandas/blob/main/docs/tutorials/data/3eiy_stripped.pdb.gz?raw=true)]" ] } ], "metadata": { "anaconda-cloud": {}, "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.9.7" }, "toc": { "base_numbering": 1, "nav_menu": {}, "number_sections": true, "sideBar": true, "skip_h1_title": false, "title_cell": "Table of Contents", "title_sidebar": "Contents", "toc_cell": false, "toc_position": {}, "toc_section_display": true, "toc_window_display": false } }, "nbformat": 4, "nbformat_minor": 4 }