Pandas ffill() with average of before missing value and after missing value

Your logic is actually what interpolate is doing by default (method='linear'):

df.interpolate()

Output:

     val
0    1.0
1    2.0
2    3.0
3    4.0
4    5.0
5    7.0
6    9.0
7   11.0
8    1.0
9    2.0
10   6.0
11   9.0
12  12.0
13  15.0