# Table Of Contents

- Introduction
- Series

2.1 Series Creation

2.2 Series Basic Operations

2.3 Series Basic Indexing

**2.4 Series Overwriting Data**

2.5 Series Apply

2.6 Series Concatenation

2.7 Series Boolean Indexing

2.8 Series View Vs Copy

2.9 Series Missing Values

2.10 Series Challenges

```
import numpy as np
import pandas as pd
```

Now that we know how to access data from a series using an index, overwriting data is pretty straight-forward.

For example, if you have the series `foo = ['a','b','c','d','e']`

with index labels `[10,40,50,30,20]`

and you want to change the 2nd element to `'w'`

, you can use

```
foo = pd.Series(['a', 'b', 'c', 'd', 'e'], index=[10, 40, 50, 30, 20])
foo.iloc[1] = 'w'
print(foo)
## 10 a
## 40 w
## 50 c
## 30 d
## 20 e
## dtype: object
```

If you wanted to switch the 1st, 2nd and 3rd elements to `'x'`

you could do

```
foo.iloc[[0, 1, 2]] = 'x'
# or use slicing
foo.iloc[:3] = 'x'
print(foo)
## 10 x
## 40 x
## 50 x
## 30 d
## 20 e
## dtype: object
```

And obviously, you can do the same exact operations using the index labels with foo.loc

```
foo.loc[40] = 'w'
foo.loc[[10, 40, 50]] = 'x'
foo.loc[10:50] = 'x'
```

What if you wanted to overwrite the entire series with a new set of values like the ones in this array?

`new_vals = np.array([5, 10, 15, 20, 25])`

Your first instinct might be to overwrite the entire `foo`

variable like this, but then you’d lose `foo`

’s index.

```
foo = pd.Series(new_vals)
print(foo)
## 0 5
## 1 10
## 2 15
## 3 20
## 4 25
## dtype: int64
```

Instead, use slicing to select and overwrite `foo`

’s values without overwriting its index.

```
foo.iloc[:] = new_vals
print(foo)
## 10 5
## 40 10
## 50 15
## 30 20
## 20 25
## dtype: int64
```

Now suppose you have two series of 4 values each, whose indices are different but share a couple common values

```
x = pd.Series([10, 20, 30, 40])
y = pd.Series([1, 11, 111, 1111], index=[7,3,2,0])
```

What do you think the result of doing something like this will be?

`x.loc[[0, 1]] = y`

This one’s a bit strange to get used to, but when you see the result, it’s pretty clear to understand what’s happening.

```
print(x)
## 0 1111.0
## 1 NaN
## 2 30.0
## 3 40.0
## dtype: float64
```

Pandas starts by creating a temporary subset of `x`

with index labels 0 and 1. Then it looks for matching labels in `y`

to use to overwrite `x`

. Since `x`

’s label 1 doesn’t doesn’t match any elements in `y`

, pandas gives it the value NaN. And since NaN only exists as a floating point value, pandas has to cast the entire series from ints to floats.

Also note that we could do the same exact thing using slicing.

`x.iloc[:2] = y`

If we try to do this using a NumPy array on the right-hand-side, we’ll get an error because when the right-hand-side is a NumPy array, pandas tries to assign each element of the NumPy array to the Series subset on an element-by-element basis and the NumPy array has more elements than the Series subset.

`x.loc[[0, 1]] = y.to_numpy() # error`

If the NumPy array on the right-hand-side is the same length as the Series subset on the left-hand-side, this would work.

```
x.loc[[0, 1]] = y.to_numpy()[:2]
print(x)
## 0 1.0
## 1 11.0
## 2 30.0
## 3 40.0
## dtype: float64
```