python, pandas - dataframe with time, create shifted data - python

I have a DataFrame:
df = pd.DataFrame(
np.random.rand(10, 3),
columns='sensor_id|unix_timestamp|value'.split('|'))
I want to create 5 more columns in which each new column is a shifted version of the value column.
sensor_id unix_timestamp value value_shift_0 value_shift_1 value_shift_2 value_shift_3 value_shift_4
0 0.901001 0.036683 0.945908 NaN NaN NaN NaN NaN
1 0.751759 0.038600 0.117308 NaN NaN NaN NaN NaN
2 0.737604 0.484417 0.602733 NaN NaN NaN NaN NaN
3 0.259865 0.522115 0.074188 NaN NaN NaN NaN NaN
4 0.932359 0.662560 0.648445 NaN NaN NaN NaN NaN
5 0.114668 0.066766 0.285553 NaN NaN NaN NaN NaN
6 0.795851 0.565259 0.888404 NaN NaN NaN NaN NaN
7 0.082534 0.355506 0.671816 NaN NaN NaN NaN NaN
8 0.336648 0.651789 0.859373 NaN NaN NaN NaN NaN
9 0.917073 0.842281 0.458542 NaN NaN NaN NaN NaN
But I don't know how to fill in with the appropriated shifted value columns.

pd.concat with a dictionary comprehension along with join
df.join(
pd.concat(
{'value_shift_{}'.format(i): df.value.shift(i) for i in range(5)},
axis=1))
alternative with numpy
def multi_shift(s, n):
a = np.arange(len(s))
i = (a[:, None] - a[:n]).ravel()
e = np.empty(i.shape)
e.fill(np.nan)
w = np.where(i >= 0)
e[w] = df.value.values[i[w]]
return pd.DataFrame(e.reshape(10, -1),
s.index, ['shift_%i' % s for s in range(n)])
df.join(multi_shift(df.value, n))
timing

Related

Changing the Values of a Multi-Index Dataframe

I have a multi-index dataframe that is set up as follows:
index = pd.MultiIndex.from_product([['A','B','C'], ['x','y', 'z']])
multi_index = pd.DataFrame(np.nan, index=np.arange(10), columns=index)
Which produces the following output:
A B C
x y z x y z x y z
0 NaN NaN NaN NaN NaN NaN NaN NaN NaN
1 NaN NaN NaN NaN NaN NaN NaN NaN NaN
2 NaN NaN NaN NaN NaN NaN NaN NaN NaN
3 NaN NaN NaN NaN NaN NaN NaN NaN NaN
4 NaN NaN NaN NaN NaN NaN NaN NaN NaN
5 NaN NaN NaN NaN NaN NaN NaN NaN NaN
6 NaN NaN NaN NaN NaN NaN NaN NaN NaN
7 NaN NaN NaN NaN NaN NaN NaN NaN NaN
8 NaN NaN NaN NaN NaN NaN NaN NaN NaN
9 NaN NaN NaN NaN NaN NaN NaN NaN NaN
I am trying to fill the values of the multi-index data frame with values. As a toy example, what I've tried to do is change the value of ['A','x',0] as follows:
multi_index['A']['x'].loc[0] = 65.2
However, I receive a 'SettingWithCopyWarning', which makes sense to me. I've also tried
multi_index['A'].iloc[[1],0] = 65.2
and received the same warning.
Is there a way one can change the values of a multi-index dataframe on a entry-by-entry basis? I.E changing the 0th index of ['A','x']?
Try:
multi_index.loc[0, ('A', 'x')] = 65.2
You can use tuples with loc for index labelling to access your multiindex columns or rows.
Or you can use iloc like this using integer index position selection, for example 2 here is the third column:
multi_index.iloc[0, 2] = 70.3
Output:
A B C
x y z x y z x y z
0 65.2 NaN 70.3 NaN NaN NaN NaN NaN NaN
1 NaN NaN NaN NaN NaN NaN NaN NaN NaN
2 NaN NaN NaN NaN NaN NaN NaN NaN NaN
3 NaN NaN NaN NaN NaN NaN NaN NaN NaN
4 NaN NaN NaN NaN NaN NaN NaN NaN NaN
5 NaN NaN NaN NaN NaN NaN NaN NaN NaN
6 NaN NaN NaN NaN NaN NaN NaN NaN NaN
7 NaN NaN NaN NaN NaN NaN NaN NaN NaN
8 NaN NaN NaN NaN NaN NaN NaN NaN NaN
9 NaN NaN NaN NaN NaN NaN NaN NaN NaN

Set Column Value Based on Calculate Condition from Each Row

I have a empty dataframe as
columns_name = list(str(i) for i in range(10))
dfa = pd.DataFrame(columns=columns_name, index=['A', 'B', 'C', 'D'])
dfa['Count'] = [10, 6, 9, 4]
0
1
2
3
4
5
6
7
8
9
Count
A
NaN
NaN
NaN
NaN
NaN
NaN
NaN
NaN
NaN
NaN
10
B
NaN
NaN
NaN
NaN
NaN
NaN
NaN
NaN
NaN
NaN
6
C
NaN
NaN
NaN
NaN
NaN
NaN
NaN
NaN
NaN
NaN
9
D
NaN
NaN
NaN
NaN
NaN
NaN
NaN
NaN
NaN
NaN
4
I want to replace Nan values with a symbol with the difference of max(Count) - Current(max).
So, the final result will look like.
0
1
2
3
4
5
6
7
8
9
Count
A
NaN
NaN
NaN
NaN
NaN
NaN
NaN
NaN
NaN
NaN
10
B
NaN
NaN
NaN
NaN
NaN
NaN
-
-
-
-
6
C
NaN
NaN
NaN
NaN
NaN
NaN
NaN
NaN
NaN
-
9
D
NaN
NaN
NaN
NaN
-
-
-
-
-
-
4
I am stuck at
dfa.at[dfa.index, [str(col) for col in list(range(dfa['Count'].max() - dfa['Count']))]] = '-'
and getting KeyError: 'Count'
Actually, your this part of the code dfa.at[dfa.index, [str(col) for col in list(range(dfa['Count'].max() - dfa['Count']))]] = '-' has issue.
Just try to create the list which you are trying to use inside comprehension
list(range(dfa['Count'].max() - dfa['Count']))
It'll throw TypeError
If you notice, you'll figure out that (dfa['Count'].max() - dfa['Count']) will give following series:
A 0
B 4
C 1
D 6
And since you're trying to pass a series to python's range function, it will throw the error.
One possible solution might be:
for index, cols in zip(dfa.index, [list(map(str, col)) for col in (dfa).apply(lambda x: list(range(x['Count'], dfa['Count'].max())), axis=1).values]):
dfa.loc[index, cols] = '-'
OUTPUT:
Out[315]:
0 1 2 3 4 5 6 7 8 9 Count
A NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN 10
B NaN NaN NaN NaN NaN NaN - - - - 6
C NaN NaN NaN NaN NaN NaN NaN NaN NaN - 9
D NaN NaN NaN NaN - - - - - - 4
Broadcasting is also an option:
import pandas as pd
import numpy as np
columns_name = list(str(i) for i in range(10))
dfa = pd.DataFrame(columns=columns_name, index=['A', 'B', 'C', 'D'])
dfa['Count'] = [10, 6, 9, 4]
# Broadcast based on column index (Excluding Count)
m = (
dfa['Count'].to_numpy()[:, None] == np.arange(0, dfa.shape[1] - 1)
).cumsum(axis=1).astype(bool)
# Grab Columns To Update
non_count_columns = dfa.columns[dfa.columns != 'Count']
# Update based on mask
dfa[non_count_columns] = dfa[non_count_columns].mask(m, '-')
print(dfa)
Output:
0 1 2 3 4 5 6 7 8 9 Count
A NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN 10
B NaN NaN NaN NaN NaN NaN - - - - 6
C NaN NaN NaN NaN NaN NaN NaN NaN NaN - 9
D NaN NaN NaN NaN - - - - - - 4

Selecting multiple columns in pandas that start with similar letter

Low S0.0 S1.0 S2.0 S3.0 S4.0 S5.0 S6.0 S7.0 S8.0 S9.0 S10.0 S11.0
0 55 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
1 60 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
2 78 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
3 12 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
4 77 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
I have the following code to check if any of the "S" columns are near to "close":
level=0.035
cond = np.isclose(df.Low, df['S0.0'], rtol=level) | np.isclose(df.Low, df['S1.0'], rtol=level) | ...
df['ST'] = np.where(cond, 100, 0)
But this looks too manual, is there some way to attribute all the S columns without specifically naming all of them? Also considering that these columns keep on changing so specifically calling every column sometimes gives an error. THANKS!
I think a solution can be as follows:
from itertools import repeat
from operator import or_
selected_columns = [c for c in df.columns if c.startswith('s')]
cond = None
for low_serie, sel_serie in zip(repeat(df.Low), [df[selected_column] for selected_column in selected_columns]):
if cond is None:
cond = np.isclose(low_serie, sel_serie, rtol=level)
continue
cond = or_(cond, np.isclose(low_serie, sel_serie, rtol=level))
You have to pay attention to the condition to select the columns names. I put as an example if c.startswith('s').

How to return df with non-nan values of unique column Pandas DataFrame Pythonically

I have got the following dataframe, in which each column contains a set of values, and each index is only used once. However, I would like to get a completely filled dataframe. In order to do that I need to select, from each column, an X amount of values, in which X is the length of the column with the least non-nan values (in this case column '1.0').
>>> stat_df_iws
iws_w -2.0 -1.0 0.0 1.0
0 0.363567 NaN NaN NaN
1 0.183698 NaN NaN NaN
2 NaN -0.337931 NaN NaN
3 -0.231770 NaN NaN NaN
4 NaN 0.544836 NaN NaN
5 NaN -0.377620 NaN NaN
6 NaN NaN -0.428396 NaN
7 NaN NaN -0.443317 NaN
8 NaN -0.268033 NaN NaN
9 NaN 0.246714 NaN NaN
10 NaN NaN -0.503887 NaN
11 NaN NaN NaN -0.298935
12 NaN -0.252775 NaN NaN
13 NaN -0.447757 NaN NaN
14 -0.650598 NaN NaN NaN
15 -0.660542 NaN NaN NaN
16 NaN -0.952041 NaN NaN
17 -0.667356 NaN NaN NaN
18 -0.920873 NaN NaN NaN
19 NaN -0.537657 NaN NaN
20 NaN NaN -0.525121 NaN
21 NaN NaN NaN -0.619755
22 NaN -0.652138 NaN NaN
23 NaN -0.924181 NaN NaN
24 NaN -0.665720 NaN NaN
25 NaN NaN -0.336841 NaN
26 -0.428931 NaN NaN NaN
27 NaN -0.348248 NaN NaN
28 NaN 0.781024 NaN NaN
29 0.110727 NaN NaN NaN
... ... ... ... ...
I've achieved this with the following code, but it is not a very pythonic way of solving this.
def get_non_null_from_pivot(df):
lngth = min(list(len(col.dropna()) for ind, col in df.iteritems()))
df = pd.concat([df.loc[:,-2.0].dropna().head(lngth).reset_index(drop=True),\
df.loc[:,-1.0].dropna().head(lngth).reset_index(drop=True),\
df.loc[:,0.0].dropna().head(lngth).reset_index(drop=True),\
df.loc[:,1.0].dropna().head(lngth).reset_index(drop=True)], \
axis=1)
Is there a simpler way to achieve the same goal, so that I can more automatically repeat this step for other dataframes? Preferably without for-loops, for efficiency reasons.
I've made the function a little shorter by looping through the columns, and it seems to work perfectly.
def get_non_null_from_pivot_short(df):
lngth = min(list(len(col.dropna()) for ind, col in df.iteritems()))
df = pd.concat(list(df.loc[:,col].dropna().head(lngth).reset_index(drop=True) for col in df), \
axis=1)
return df

Broadcasting Error Pandas

I have a dataframe with 4 columns. I want to do an element-wise division of the first 3 columns by the value in 4th column
I tried:
df2 = pd.DataFrame(df.ix[:,['col1', 'col2', 'col3']].values / df.col4.values)
And I got this error:
ValueError: operands could not be broadcast together with shapes (19,3) (19,)
My solution was:
df2 = pd.DataFrame(df.ix[:,['col1', 'col2', 'col3']].values / df.col4.values.reshape(19,1))
This worked as I wanted, but to be robust for different numbers of rows I would need to do:
.reshape(len(df),1)
It just seems an ugly way to have to do something - is there a better way around the array shape being (19,) it seems odd that it has no second dimension.
Best Regards,
Ben
You can just do div and pass axis=0 to force the division to be performed column-wise:
df2 = pd.DataFrame(df.ix[:,['col1', 'col2', 'col3']].div(df.col4, axis=0))
Your error is because the division using / is being performed on the minor axis which in this case is the row axis and there is no direct alignment, see this example:
In [220]:
df = pd.DataFrame(columns=list('abcd'), data = np.random.randn(8,4))
df
Out[220]:
a b c d
0 1.074803 0.173520 0.211027 1.357138
1 1.418757 -1.879024 0.536826 1.006160
2 -0.029716 -1.146178 0.100900 -1.035018
3 0.314665 -0.773723 -1.170653 0.648740
4 -0.179666 1.291836 -0.009614 0.392149
5 0.264599 -0.057409 -1.425638 1.024098
6 -0.106062 1.824375 0.595974 1.167115
7 0.601544 -1.237881 0.106854 -1.276829
In [221]:
df.ix[:,['a', 'b', 'c']]/df['d']
Out[221]:
a b c 0 1 2 3 4 5 6 7
0 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
1 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
2 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
3 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
4 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
5 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
6 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
7 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
This isn't obvious until you understand how broadcasting works.

Categories

Resources