Pandas 列整形:将值左对齐(忽略外部零)
Pandas column reshaping: aligning the values to the left (ignoring the outside zeros)
我有一组数据,可以预测未来 10 周左右我需要的燃料量。我将其全部设置在一个显示为楼梯日期的数据框中。这意味着,我越接近一周的最后一个条目,值就越准确。我想删除所有缺失值并忽略确切日期,这样我就可以查看与预测周的距离相关的预测。
输入数据帧:
Index 2020-01 2020-02 2020-03 2020-04 2020-05 2020-06
1. 10 10 5 0 0 0
2. 0 5 5 10 0 0
3. 0 0 10 4 3 0
4. 0 0 0 1 7 6
结果应该是:
Index W1 W2 W3
1. 10 10 5
2. 5 5 10
3. 10 4 3
4. 1 7 6
非常感谢
您可以用 NaN 替换零并重置每行的系列:
df2 = (
df.replace(0,float('nan'))
.apply(lambda s: s.dropna().reset_index(drop=True), axis=1)
.astype(int)
)
df2.columns = df2.columns.map(lambda x: f'W{x+1}')
输出:
W1 W2 W3
1.0 10 10 5
2.0 5 5 10
3.0 10 4 3
4.0 1 7 6
确保仅删除外部 0
对于这个例子,我将 2.
/2020-03
设置为 0
您可以使用 ffill
+bfill
来计算掩码:
m = df.ne(0)
m1 = m.where(m).bfill(axis=1)
m2 = m.where(m).ffill(axis=1)
df2 = (
df.where(m1&m2) # internal 0s are those True for both ffill/bfill
.apply(lambda s: s.dropna().reset_index(drop=True), axis=1)
.astype(int)
)
df2.columns = df2.columns.map(lambda x: f'W{x+1}')
输出:
W1 W2 W3
1.0 10 10 5
2.0 5 0 10
3.0 10 4 3
4.0 1 7 6
使用 justify
函数删除非 0
值,最后删除仅填充 0
值的列:
c = [f'W{x + 1}' for x, _ in enumerate(df.columns)]
df = pd.DataFrame(justify(df.to_numpy()), index=df.index, columns=c)
df = df.loc[:, df.ne(0).any()]
print (df)
W1 W2 W3
Index
1.0 10 10 5
2.0 5 5 10
3.0 10 4 3
4.0 1 7 6
##
def justify(a, invalid_val=0, axis=1, side='left'):
"""
Justifies a 2D array
Parameters
----------
A : ndarray
Input array to be justified
axis : int
Axis along which justification is to be made
side : str
Direction of justification. It could be 'left', 'right', 'up', 'down'
It should be 'left' or 'right' for axis=1 and 'up' or 'down' for axis=0.
"""
if invalid_val is np.nan:
mask = ~np.isnan(a)
else:
mask = a!=invalid_val
justified_mask = np.sort(mask,axis=axis)
if (side=='up') | (side=='left'):
justified_mask = np.flip(justified_mask,axis=axis)
out = np.full(a.shape, invalid_val)
if axis==1:
out[justified_mask] = a[mask]
else:
out.T[justified_mask.T] = a.T[mask.T]
return out
使用自定义函数和apply
将是最直接易懂的方法:
def merge_row(row):
vals = [v for v in row.values if v != 0]
return pd.Series({f'W{i}': v for i, v in enumerate(vals)})
df.apply(merge_row, axis=1)
结果:
W0 W1 W2
Index
1.0 10 10 5
2.0 5 5 10
3.0 10 4 3
4.0 1 7 6
您可以使用 numpy 按 0/非 0 状态排序并重建 DataFrame:
import numpy as np
a = df.to_numpy()
b = a==0
idx = np.argsort(b, axis=1)
n_cols = (~b).sum(1).max()
pd.DataFrame(np.take_along_axis(a, idx, axis=1)[:, :n_cols],
columns=[f'W{i}' for i in np.arange(n_cols)+1],
index=df.index
)
输出:
W1 W2 W3
1.0 10 10 5
2.0 5 5 10
3.0 10 4 3
4.0 1 7 6
处理内部零:
import numpy as np
a = df.to_numpy()
m1 = a.cumsum(1) != 0
m2 = np.fliplr(np.fliplr(a).cumsum(1)) != 0
m = (m1&m2)
idx = np.argsort(~m, axis=1)
n_cols = m.sum(1).max()
pd.DataFrame(np.take_along_axis(a, idx, axis=1)[:, :n_cols],
columns=[f'W{i}' for i in np.arange(n_cols)+1],
index=df.index
)
我有一组数据,可以预测未来 10 周左右我需要的燃料量。我将其全部设置在一个显示为楼梯日期的数据框中。这意味着,我越接近一周的最后一个条目,值就越准确。我想删除所有缺失值并忽略确切日期,这样我就可以查看与预测周的距离相关的预测。
输入数据帧:
Index 2020-01 2020-02 2020-03 2020-04 2020-05 2020-06
1. 10 10 5 0 0 0
2. 0 5 5 10 0 0
3. 0 0 10 4 3 0
4. 0 0 0 1 7 6
结果应该是:
Index W1 W2 W3
1. 10 10 5
2. 5 5 10
3. 10 4 3
4. 1 7 6
非常感谢
您可以用 NaN 替换零并重置每行的系列:
df2 = (
df.replace(0,float('nan'))
.apply(lambda s: s.dropna().reset_index(drop=True), axis=1)
.astype(int)
)
df2.columns = df2.columns.map(lambda x: f'W{x+1}')
输出:
W1 W2 W3
1.0 10 10 5
2.0 5 5 10
3.0 10 4 3
4.0 1 7 6
确保仅删除外部 0
对于这个例子,我将 2.
/2020-03
设置为 0
您可以使用 ffill
+bfill
来计算掩码:
m = df.ne(0)
m1 = m.where(m).bfill(axis=1)
m2 = m.where(m).ffill(axis=1)
df2 = (
df.where(m1&m2) # internal 0s are those True for both ffill/bfill
.apply(lambda s: s.dropna().reset_index(drop=True), axis=1)
.astype(int)
)
df2.columns = df2.columns.map(lambda x: f'W{x+1}')
输出:
W1 W2 W3
1.0 10 10 5
2.0 5 0 10
3.0 10 4 3
4.0 1 7 6
使用 justify
函数删除非 0
值,最后删除仅填充 0
值的列:
c = [f'W{x + 1}' for x, _ in enumerate(df.columns)]
df = pd.DataFrame(justify(df.to_numpy()), index=df.index, columns=c)
df = df.loc[:, df.ne(0).any()]
print (df)
W1 W2 W3
Index
1.0 10 10 5
2.0 5 5 10
3.0 10 4 3
4.0 1 7 6
##
def justify(a, invalid_val=0, axis=1, side='left'):
"""
Justifies a 2D array
Parameters
----------
A : ndarray
Input array to be justified
axis : int
Axis along which justification is to be made
side : str
Direction of justification. It could be 'left', 'right', 'up', 'down'
It should be 'left' or 'right' for axis=1 and 'up' or 'down' for axis=0.
"""
if invalid_val is np.nan:
mask = ~np.isnan(a)
else:
mask = a!=invalid_val
justified_mask = np.sort(mask,axis=axis)
if (side=='up') | (side=='left'):
justified_mask = np.flip(justified_mask,axis=axis)
out = np.full(a.shape, invalid_val)
if axis==1:
out[justified_mask] = a[mask]
else:
out.T[justified_mask.T] = a.T[mask.T]
return out
使用自定义函数和apply
将是最直接易懂的方法:
def merge_row(row):
vals = [v for v in row.values if v != 0]
return pd.Series({f'W{i}': v for i, v in enumerate(vals)})
df.apply(merge_row, axis=1)
结果:
W0 W1 W2
Index
1.0 10 10 5
2.0 5 5 10
3.0 10 4 3
4.0 1 7 6
您可以使用 numpy 按 0/非 0 状态排序并重建 DataFrame:
import numpy as np
a = df.to_numpy()
b = a==0
idx = np.argsort(b, axis=1)
n_cols = (~b).sum(1).max()
pd.DataFrame(np.take_along_axis(a, idx, axis=1)[:, :n_cols],
columns=[f'W{i}' for i in np.arange(n_cols)+1],
index=df.index
)
输出:
W1 W2 W3
1.0 10 10 5
2.0 5 5 10
3.0 10 4 3
4.0 1 7 6
处理内部零:
import numpy as np
a = df.to_numpy()
m1 = a.cumsum(1) != 0
m2 = np.fliplr(np.fliplr(a).cumsum(1)) != 0
m = (m1&m2)
idx = np.argsort(~m, axis=1)
n_cols = m.sum(1).max()
pd.DataFrame(np.take_along_axis(a, idx, axis=1)[:, :n_cols],
columns=[f'W{i}' for i in np.arange(n_cols)+1],
index=df.index
)