# Atom Groups¶

The example below shows how to build an `AtomGroup` from scratch. We start by importing everything from the ProDy package and the NumPy package:

```In [1]: from prody import *

In [2]: from numpy import *

In [3]: from pylab import *

In [4]: ion()
```

## Building an Atom Group¶

The best way to start constructing an `AtomGroup` is by setting the coordinates first. Number of atoms will be automatically set according to the size of the coordinate data array:

```In [5]: wtr1 = AtomGroup('Water')

In [6]: wtr1
Out[6]: <AtomGroup: Water (0 atoms; no coordinates)>

In [7]: coords = array([[1, 0, 0], [0, 0, 0], [0, 0, 1]], dtype=float)

In [8]: coords
Out[8]:
array([[1., 0., 0.],
[0., 0., 0.],
[0., 0., 1.]])

In [9]: wtr1.setCoords(coords)

In [10]: wtr1
Out[10]: <AtomGroup: Water (3 atoms)>
```

### Attributes¶

Attributes must be passed in a list or an array whose size is the same as the number of atoms.

```In [11]: wtr1.setNames(['H', 'O', 'H'])

In [12]: wtr1.setResnums([1, 1, 1])

In [13]: wtr1.setResnames(['WAT', 'WAT', 'WAT'])
```

Accessing data will return a copy of the data. This means you can manipulate the return values safely without affecting the original values. For example:

```In [14]: names = wtr1.getNames()

In [15]: names[0] = names[0] + '1'

In [16]: names
Out[16]: array(['H1', 'O', 'H'], dtype='|S6')
```
```In [17]: wtr1.getNames()
Out[17]: array(['H', 'O', 'H'], dtype='|S6')
```

### Atoms¶

Atoms are represented by instance of `Atom`.

#### Iteration¶

Atoms in an `AtomGroup` can be iterated over

```In [18]: for a in wtr1:
....:     print(a)
....:
Atom H (index 0)
Atom O (index 1)
Atom H (index 2)
```

#### Indexing¶

Atoms in an atom group can be accessed via indexing:

```In [19]: a = wtr1[0]

In [20]: a
Out[20]: <Atom: H from Water (index 0)>
```
```In [21]: a.getCoords()
Out[21]: array([1., 0., 0.])
```

### Coordinate sets¶

Let’s add another coordinate set to the atom group:

```In [22]: wtr1.addCoordset(array([[0, 1, 0], [0, 0, 0], [0, 0, 1.1]], dtype=float))

In [23]: wtr1
Out[23]: <AtomGroup: Water (3 atoms; active #0 of 2 coordsets)>
```

Note that the number of coordinate sets is now 2, but the active coordinate set index is still 0. Active coordinate set index can be changed for `AtomGroup` as follows:

```In [24]: a.setACSIndex(1)

In [25]: a
Out[25]: <Atom: H from Water (index 0; active #1 of 2 coordsets)>
```

Changing active coordinate set for an atom group, does not affect the active coordinate set of the atom group:

```In [26]: wtr1
Out[26]: <AtomGroup: Water (3 atoms; active #0 of 2 coordsets)>
```

Coordinates for the atom group will be returned from the active coordinate set

```In [27]: a.getCoords()
Out[27]: array([0., 1., 0.])
```
```In [28]: a.setACSIndex(0)

In [29]: a.getCoords()
Out[29]: array([1., 0., 0.])
```

#### Iterations¶

Coordinate sets can also be iterated over for `Atom` and `AtomGroup` instances:

```In [30]: for xyz in a.iterCoordsets():
....:     print(xyz)
....:
[1. 0. 0.]
[0. 1. 0.]
```

### Copying and Merging¶

Now let’s make another copy of this water:

```In [31]: wtr2 = wtr1.copy()

In [32]: wtr2
Out[32]: <AtomGroup: Water (3 atoms; active #0 of 2 coordsets)>
```

#### Translate copy¶

Let’s translate the coordinates of wtr2 so that it does not overlap with wtr1

```In [33]: wtr2.setCoords(wtr2.getCoords() + 2)

In [34]: wtr2.getCoords()
Out[34]:
array([[3., 2., 2.],
[2., 2., 2.],
[2., 2., 3.]])
```

Above operation only translated the coordinate set at index 0

```In [35]: wtr2.setACSIndex(1)

In [36]: wtr2.getCoords()
Out[36]:
array([[0. , 1. , 0. ],
[0. , 0. , 0. ],
[0. , 0. , 1.1]])

In [37]: wtr2.setCoords(wtr2.getCoords() + 2)  # translate the 2nd coordset as well
```

#### Change attributes¶

Before we merge wtr1 and wtr2, let’s change resid’s of wtr2:

```In [38]: wtr2.setResnums( [2, 2, 2] )

In [39]: wtr2.getResnums()
Out[39]: array([2, 2, 2])
```

We can do this in an alternate way too:

```In [40]: wtr2.select('all').setResnums(2)

In [41]: wtr2.getResnums()
Out[41]: array([2, 2, 2])
```

#### Merge two copies¶

Let’s merge the two water atom groups:

```In [42]: wtrs = wtr1 + wtr2

In [43]: wtrs
Out[43]: <AtomGroup: Water + Water (6 atoms; active #0 of 2 coordsets)>

In [44]: wtrs.getCoords()
Out[44]:
array([[1., 0., 0.],
[0., 0., 0.],
[0., 0., 1.],
[3., 2., 2.],
[2., 2., 2.],
[2., 2., 3.]])

In [45]: wtrs.getNames()
Out[45]: array(['H', 'O', 'H', 'H', 'O', 'H'], dtype='|S6')

In [46]: wtrs.getResnums()
Out[46]: array([1, 1, 1, 2, 2, 2])
```

### Hierarchical views¶

Hierarchical views of atom groups are represented by `HierView`.

Residues (and also chains) in an atom group can also be iterated over

```In [47]: for res in wtrs.getHierView().iterResidues():
....:     print(res)
....:
WAT 1
WAT 2
```

### Renaming an atom group¶

Finally, it’s is possible to change the name of wtrs from “Water + Water” to something shorter:

```In [48]: wtrs.setTitle('2Waters')

In [49]: wtrs
Out[49]: <AtomGroup: 2Waters (6 atoms; active #0 of 2 coordsets)>
```

## Storing data in AtomGroup¶

Now let’s get an atom group from a PDB file:

```In [50]: structure = parsePDB('1p38')
```

In addition to what’s in a PDB file, you can store arbitrary atomic attributes in `AtomGroup` objects.

### Set a new attribute¶

For the purposes of this example, we will manufacture atomic data by dividing the residue number of each atom by 10:

```In [51]: myresnum = structure.getResnums() / 10.0
```

We will add this to the atom group using `AtomGroup.setData()` method by passing a name for the attribute and the data:

```In [52]: structure.setData('myresnum', myresnum)
```

We can check if a custom atomic attribute exists using `AtomGroup.isDataLabel()` method:

```In [53]: structure.isDataLabel('myresnum')
Out[53]: True
```

### Access subset of data¶

Custom attributes can be accessed from selections:

```In [54]: calpha = structure.calpha

In [55]: calpha.getData('myresnum')
Out[55]:
array([ 0.4,  0.5,  0.6,  0.7,  0.8,  0.9,  1. ,  1.1,  1.2,  1.3,  1.4,
1.5,  1.6,  1.7,  1.8,  1.9,  2. ,  2.1,  2.2,  2.3,  2.4,  2.5,
2.6,  2.7,  2.8,  2.9,  3. ,  3.1,  3.2,  3.3,  3.4,  3.5,  3.6,
3.7,  3.8,  3.9,  4. ,  4.1,  4.2,  4.3,  4.4,  4.5,  4.6,  4.7,
4.8,  4.9,  5. ,  5.1,  5.2,  5.3,  5.4,  5.5,  5.6,  5.7,  5.8,
5.9,  6. ,  6.1,  6.2,  6.3,  6.4,  6.5,  6.6,  6.7,  6.8,  6.9,
7. ,  7.1,  7.2,  7.3,  7.4,  7.5,  7.6,  7.7,  7.8,  7.9,  8. ,
8.1,  8.2,  8.3,  8.4,  8.5,  8.6,  8.7,  8.8,  8.9,  9. ,  9.1,
9.2,  9.3,  9.4,  9.5,  9.6,  9.7,  9.8,  9.9, 10. , 10.1, 10.2,
10.3, 10.4, 10.5, 10.6, 10.7, 10.8, 10.9, 11. , 11.1, 11.2, 11.3,
11.4, 11.5, 11.6, 11.7, 11.8, 11.9, 12. , 12.1, 12.2, 12.3, 12.4,
12.5, 12.6, 12.7, 12.8, 12.9, 13. , 13.1, 13.2, 13.3, 13.4, 13.5,
13.6, 13.7, 13.8, 13.9, 14. , 14.1, 14.2, 14.3, 14.4, 14.5, 14.6,
14.7, 14.8, 14.9, 15. , 15.1, 15.2, 15.3, 15.4, 15.5, 15.6, 15.7,
15.8, 15.9, 16. , 16.1, 16.2, 16.3, 16.4, 16.5, 16.6, 16.7, 16.8,
16.9, 17. , 17.1, 17.2, 17.3, 17.4, 17.5, 17.6, 17.7, 17.8, 17.9,
18. , 18.1, 18.2, 18.3, 18.4, 18.5, 18.6, 18.7, 18.8, 18.9, 19. ,
19.1, 19.2, 19.3, 19.4, 19.5, 19.6, 19.7, 19.8, 19.9, 20. , 20.1,
20.2, 20.3, 20.4, 20.5, 20.6, 20.7, 20.8, 20.9, 21. , 21.1, 21.2,
21.3, 21.4, 21.5, 21.6, 21.7, 21.8, 21.9, 22. , 22.1, 22.2, 22.3,
22.4, 22.5, 22.6, 22.7, 22.8, 22.9, 23. , 23.1, 23.2, 23.3, 23.4,
23.5, 23.6, 23.7, 23.8, 23.9, 24. , 24.1, 24.2, 24.3, 24.4, 24.5,
24.6, 24.7, 24.8, 24.9, 25. , 25.1, 25.2, 25.3, 25.4, 25.5, 25.6,
25.7, 25.8, 25.9, 26. , 26.1, 26.2, 26.3, 26.4, 26.5, 26.6, 26.7,
26.8, 26.9, 27. , 27.1, 27.2, 27.3, 27.4, 27.5, 27.6, 27.7, 27.8,
27.9, 28. , 28.1, 28.2, 28.3, 28.4, 28.5, 28.6, 28.7, 28.8, 28.9,
29. , 29.1, 29.2, 29.3, 29.4, 29.5, 29.6, 29.7, 29.8, 29.9, 30. ,
30.1, 30.2, 30.3, 30.4, 30.5, 30.6, 30.7, 30.8, 30.9, 31. , 31.1,
31.2, 31.3, 31.4, 31.5, 31.6, 31.7, 31.8, 31.9, 32. , 32.1, 32.2,
32.3, 32.4, 32.5, 32.6, 32.7, 32.8, 32.9, 33. , 33.1, 33.2, 33.3,
33.4, 33.5, 33.6, 33.7, 33.8, 33.9, 34. , 34.1, 34.2, 34.3, 34.4,
34.5, 34.6, 34.7, 34.8, 34.9, 35. , 35.1, 35.2, 35.3, 35.4])
```

### Make selections¶

Custom atomic attributes can be used in selections:

```In [56]: mysel = structure.select('0 < myresnum and myresnum < 10')

In [57]: mysel
Out[57]: <Selection: '0 < myresnum and myresnum < 10' from 1p38 (788 atoms)>
```

This gives the same result as the following selection:

```In [58]: structure.select('0 < resnum and resnum < 100') == mysel
Out[58]: True
```

### Save attributes¶

It is not possible to save custom attributes in PDB files, but `saveAtoms()` function can be used them to save in disk for later use:

```In [59]: saveAtoms(structure)
Out[59]: '1p38.ag.npz'
```

Let’s load it using `loadAtoms()` function:

```In [60]: structure = loadAtoms('1p38.ag.npz')

In [61]: structure.getData('myresnum')
Out[61]: array([ 0.4,  0.4,  0.4, ..., 77.1, 77.3, 77.6])
```

### Delete an attribute¶

Finally, when done with an attribute, it can be deleted using `AtomGroup.delData()` method:

```In [62]: structure.delData('myresnum')
Out[62]: array([ 0.4,  0.4,  0.4, ..., 77.1, 77.3, 77.6])
```