根据多级数据框中列中的数据存在来排列数据框
Arrage dataframe based on the data presence in columns in multilevel dataframe
我在 pandas df
中有一个多级列,索引为 appid
如下:
year |2016 2017 2018 2019 2016 2017 2018 2019
|ttl ttl ttl ttl tta tta tta tta
-----------------------------------------------------------------
appid |
75787 |NaN 227.0 470.0 426.0 NaN 25.0 23.0 21.0
146306 |NaN 858.0 226.0 NaN NaN 14.0 35.0 NaN
159479 |NaN NaN 0.0 NaN NaN NaN 3.5 NaN
163618 |NaN 0.0 650.0 100.0 NaN 12.0 14.6 123.0
215968 |23.0 0.0 NaN NaN 45.0 2.0 NaN NaN
我想将此 df
转换为可以通过存在的最新年份条目进行排序的方式。例如。
Year |P2Y PY LY P2Y PY LY
|ttl ttl ttl tta tta tta
----------------------------------------------------
appid |
75787 |227.0 470.0 426.0 25.0 23.0 21.0
146306 |NaN 858.0 226.0 NaN 14.0 35.0
159479 |NaN NaN 0.0 NaN NaN 3.5
163618 |0.0 650.0 100.0 12.0 14.6 123.0
215968 |NaN 23.0 0.0 NaN 45.0 2.0
您可以尝试处理转置数据集并使用 shift
:
df.T \
.apply(lambda x: x.shift(len(x) - x.index.get_loc(x.last_valid_index()) - 1)) \
.T \
.dropna(how='all', axis='columns'))
说明
使用 .T
转置数据集
将每列移动指定数量的 NaN
列末尾的值
在每列上使用 apply
使用 last_valid_index
with get_loc
查找最后一个不是 NaN
的值。有关此步骤的更多详细信息,请参阅 Locate first and last non NaN values in a Pandas DataFrame
计算步骤 2.3 和 len(x)
的行移位数。还要减去 1,因为步骤 2.2 中的索引采用上面的行索引。
使用shift
移动列
最终使用 .T
将数据集转回第 1 步
使用 dropna
和 how='all', axis='columns'
删除所有 NaN
列
代码+插图
# Step 1
print(df.T)
# 75787 146306 159479 163618 215968
# year appid
# 2016 ttl NaN NaN NaN NaN 23.0
# 2017 ttl 227.0 858.0 NaN 0.0 0.0
# 2018 ttl 470.0 226.0 0.0 650.0 NaN
# 2019 ttl 426.0 NaN NaN 100.0 NaN
# 2016 tta NaN NaN NaN NaN 45.0
# 2017 tta 25.0 14.0 NaN 12.0 2.0
# 2018 tta 23.0 35.0 3.5 14.6 NaN
# 2019 tta 21.0 NaN NaN 123.0 NaN
# Step 2.2.1
print(df.T.apply(lambda x: x.last_valid_index()))
# 75787 (2019, tta)
# 146306 (2018, tta)
# 159479 (2018, tta)
# 163618 (2019, tta)
# 215968 (2017, tta)
# dtype: object
# Step 2.2.2
print(df.T.apply(lambda x: x.index.get_loc(x.last_valid_index())))
# 75787 7
# 146306 6
# 159479 6
# 163618 7
# 215968 5
# dtype: int64
# Step 2
print(df.T.apply(lambda x: x.shift(
len(x) - x.index.get_loc(x.last_valid_index()) - 1)))
# 75787 146306 159479 163618 215968
# year appid
# 2016 ttl NaN NaN NaN NaN NaN
# 2017 ttl 227.0 NaN NaN 0.0 NaN
# 2018 ttl 470.0 858.0 NaN 650.0 23.0
# 2019 ttl 426.0 226.0 0.0 100.0 0.0
# 2016 tta NaN NaN NaN NaN NaN
# 2017 tta 25.0 NaN NaN 12.0 NaN
# 2018 tta 23.0 14.0 NaN 14.6 45.0
# 2019 tta 21.0 35.0 3.5 123.0 2.0
# Step 3
print(df.T.apply(lambda x: x.shift(
len(x) - x.index.get_loc(x.last_valid_index()) - 1)).T)
# year 2016 2017 2018 2019 2016 2017 2018 2019
# appid ttl ttl ttl ttl tta tta tta tta
# 75787 NaN 227.0 470.0 426.0 NaN 25.0 23.0 21.0
# 146306 NaN NaN 858.0 226.0 NaN NaN 14.0 35.0
# 159479 NaN NaN NaN 0.0 NaN NaN NaN 3.5
# 163618 NaN 0.0 650.0 100.0 NaN 12.0 14.6 123.0
# 215968 NaN NaN 23.0 0.0 NaN NaN 45.0 2.0
# Step 4
print(df.T.apply(lambda x: x.shift(
len(x) - x.index.get_loc(x.last_valid_index()) - 1)).T
.dropna(how='all', axis='columns'))
# year 2017 2018 2019 2017 2018 2019
# appid ttl ttl ttl tta tta tta
# 75787 227.0 470.0 426.0 25.0 23.0 21.0
# 146306 NaN 858.0 226.0 NaN 14.0 35.0
# 159479 NaN NaN 0.0 NaN NaN 3.5
# 163618 0.0 650.0 100.0 12.0 14.6 123.0
# 215968 NaN 23.0 0.0 NaN 45.0 2.0
如有必要,您可以先DataFrame.stack
for years to columns, then use justify
, filter last 3 columns, create DataFrame and reshape back by DataFrame.unstack
with DataFrame.reindex
更改列名称的顺序:
df1 = df.stack()
arr = justify(df1.to_numpy(),invalid_val=np.nan, side='right')[:, -3:]
print (arr)
[[ 25. 23. 21. ]
[227. 470. 426. ]
[ nan 14. 35. ]
[ nan 858. 226. ]
[ nan nan 3.5]
[ nan nan 0. ]
[ 12. 14.6 123. ]
[ 0. 650. 100. ]
[ nan 45. 2. ]
[ nan 23. 0. ]]
mux = pd.MultiIndex.from_product([df.columns.levels[1], ['P2Y','PY','LY']])
df2 = (pd.DataFrame(arr, index=df1.index, columns=['P2Y','PY','LY'])
.unstack()
.swaplevel(1,0, axis=1)
.reindex(mux, axis=1))
print (df2)
tta ttl
P2Y PY LY P2Y PY LY
75787 25.0 23.0 21.0 227.0 470.0 426.0
146306 NaN 14.0 35.0 NaN 858.0 226.0
159479 NaN NaN 3.5 NaN NaN 0.0
163618 12.0 14.6 123.0 0.0 650.0 100.0
215968 NaN 45.0 2.0 NaN 23.0 0.0
函数:
#
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
我在 pandas df
中有一个多级列,索引为 appid
如下:
year |2016 2017 2018 2019 2016 2017 2018 2019
|ttl ttl ttl ttl tta tta tta tta
-----------------------------------------------------------------
appid |
75787 |NaN 227.0 470.0 426.0 NaN 25.0 23.0 21.0
146306 |NaN 858.0 226.0 NaN NaN 14.0 35.0 NaN
159479 |NaN NaN 0.0 NaN NaN NaN 3.5 NaN
163618 |NaN 0.0 650.0 100.0 NaN 12.0 14.6 123.0
215968 |23.0 0.0 NaN NaN 45.0 2.0 NaN NaN
我想将此 df
转换为可以通过存在的最新年份条目进行排序的方式。例如。
Year |P2Y PY LY P2Y PY LY
|ttl ttl ttl tta tta tta
----------------------------------------------------
appid |
75787 |227.0 470.0 426.0 25.0 23.0 21.0
146306 |NaN 858.0 226.0 NaN 14.0 35.0
159479 |NaN NaN 0.0 NaN NaN 3.5
163618 |0.0 650.0 100.0 12.0 14.6 123.0
215968 |NaN 23.0 0.0 NaN 45.0 2.0
您可以尝试处理转置数据集并使用 shift
:
df.T \
.apply(lambda x: x.shift(len(x) - x.index.get_loc(x.last_valid_index()) - 1)) \
.T \
.dropna(how='all', axis='columns'))
说明
使用
.T
转置数据集
将每列移动指定数量的
NaN
列末尾的值在每列上使用
apply
使用
last_valid_index
withget_loc
查找最后一个不是NaN
的值。有关此步骤的更多详细信息,请参阅 Locate first and last non NaN values in a Pandas DataFrame计算步骤 2.3 和
len(x)
的行移位数。还要减去 1,因为步骤 2.2 中的索引采用上面的行索引。使用
shift
移动列
最终使用
.T
将数据集转回第 1 步
使用
dropna
和how='all', axis='columns'
删除所有
NaN
列
代码+插图
# Step 1
print(df.T)
# 75787 146306 159479 163618 215968
# year appid
# 2016 ttl NaN NaN NaN NaN 23.0
# 2017 ttl 227.0 858.0 NaN 0.0 0.0
# 2018 ttl 470.0 226.0 0.0 650.0 NaN
# 2019 ttl 426.0 NaN NaN 100.0 NaN
# 2016 tta NaN NaN NaN NaN 45.0
# 2017 tta 25.0 14.0 NaN 12.0 2.0
# 2018 tta 23.0 35.0 3.5 14.6 NaN
# 2019 tta 21.0 NaN NaN 123.0 NaN
# Step 2.2.1
print(df.T.apply(lambda x: x.last_valid_index()))
# 75787 (2019, tta)
# 146306 (2018, tta)
# 159479 (2018, tta)
# 163618 (2019, tta)
# 215968 (2017, tta)
# dtype: object
# Step 2.2.2
print(df.T.apply(lambda x: x.index.get_loc(x.last_valid_index())))
# 75787 7
# 146306 6
# 159479 6
# 163618 7
# 215968 5
# dtype: int64
# Step 2
print(df.T.apply(lambda x: x.shift(
len(x) - x.index.get_loc(x.last_valid_index()) - 1)))
# 75787 146306 159479 163618 215968
# year appid
# 2016 ttl NaN NaN NaN NaN NaN
# 2017 ttl 227.0 NaN NaN 0.0 NaN
# 2018 ttl 470.0 858.0 NaN 650.0 23.0
# 2019 ttl 426.0 226.0 0.0 100.0 0.0
# 2016 tta NaN NaN NaN NaN NaN
# 2017 tta 25.0 NaN NaN 12.0 NaN
# 2018 tta 23.0 14.0 NaN 14.6 45.0
# 2019 tta 21.0 35.0 3.5 123.0 2.0
# Step 3
print(df.T.apply(lambda x: x.shift(
len(x) - x.index.get_loc(x.last_valid_index()) - 1)).T)
# year 2016 2017 2018 2019 2016 2017 2018 2019
# appid ttl ttl ttl ttl tta tta tta tta
# 75787 NaN 227.0 470.0 426.0 NaN 25.0 23.0 21.0
# 146306 NaN NaN 858.0 226.0 NaN NaN 14.0 35.0
# 159479 NaN NaN NaN 0.0 NaN NaN NaN 3.5
# 163618 NaN 0.0 650.0 100.0 NaN 12.0 14.6 123.0
# 215968 NaN NaN 23.0 0.0 NaN NaN 45.0 2.0
# Step 4
print(df.T.apply(lambda x: x.shift(
len(x) - x.index.get_loc(x.last_valid_index()) - 1)).T
.dropna(how='all', axis='columns'))
# year 2017 2018 2019 2017 2018 2019
# appid ttl ttl ttl tta tta tta
# 75787 227.0 470.0 426.0 25.0 23.0 21.0
# 146306 NaN 858.0 226.0 NaN 14.0 35.0
# 159479 NaN NaN 0.0 NaN NaN 3.5
# 163618 0.0 650.0 100.0 12.0 14.6 123.0
# 215968 NaN 23.0 0.0 NaN 45.0 2.0
如有必要,您可以先DataFrame.stack
for years to columns, then use justify
, filter last 3 columns, create DataFrame and reshape back by DataFrame.unstack
with DataFrame.reindex
更改列名称的顺序:
df1 = df.stack()
arr = justify(df1.to_numpy(),invalid_val=np.nan, side='right')[:, -3:]
print (arr)
[[ 25. 23. 21. ]
[227. 470. 426. ]
[ nan 14. 35. ]
[ nan 858. 226. ]
[ nan nan 3.5]
[ nan nan 0. ]
[ 12. 14.6 123. ]
[ 0. 650. 100. ]
[ nan 45. 2. ]
[ nan 23. 0. ]]
mux = pd.MultiIndex.from_product([df.columns.levels[1], ['P2Y','PY','LY']])
df2 = (pd.DataFrame(arr, index=df1.index, columns=['P2Y','PY','LY'])
.unstack()
.swaplevel(1,0, axis=1)
.reindex(mux, axis=1))
print (df2)
tta ttl
P2Y PY LY P2Y PY LY
75787 25.0 23.0 21.0 227.0 470.0 426.0
146306 NaN 14.0 35.0 NaN 858.0 226.0
159479 NaN NaN 3.5 NaN NaN 0.0
163618 12.0 14.6 123.0 0.0 650.0 100.0
215968 NaN 45.0 2.0 NaN 23.0 0.0
函数:
#
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