Pandas 带 Groupby 的箱线图 - 在每个类别的列中绘制向量数组
Pandas Boxplot with Groupby - plot vector arrays in column per category
LTP,surface_distances
1.0,[3. 2.4494898 3.1622777 0. 0. 0. 9 ]
1.0,"[ 9.530573 9.530573 9.161782 8.731052 9.770159 9.615404
9.480876 9.399235 9.371863 9.371863 9.371863 9.371863
8.422484 8.330477 8.206197 9.068727 8.925962 8.839198
8.54362 8.206197 7.919437 7.6890116 7.5201006 7.3780055
7.130104 6.4887457 5.864883 5.2647395 9.591869 9.457006
]"
1.0,[0. 0. 0. 3.6649203 3.870587 4.065864 ]
1.0,"[ 7.95181 8.283971 7.7533436 7.1679688 7.4169073 7.854291
8.127435 8.127435 7.619655 7.0959272 6.6085405 7.3821893
7.8215146 8.032048 8.032048 8.063969 7.619655 7.1679688
6.6498694 6.988916 7.3821893 7.8215146 8. 8.
8.032048 8.127435 7.7533436 7.3798757 6.6498694 6.988916
7.3821893 7.8215146 8. 8. 8.032048 8.127435
]"
用df = pd.read_clipboard(sep=',')
读取数据
还在数据中添加了link:https://drive.google.com/file/d/1vfplqd04zs9Bigp9Vq8y6HqwDALe00NY/view?usp=sharing
我想按 df[LTP]
对 df[distances]
列进行分组,其中只有代表 2 个类别的 0 和 1 值。
我试过:
df.boxplot(column=['distances'], by='LTP', ax=ax, return_type='axes')
AND 分离成一个单独的 2 列 DataFrame。
dst_ltp = []
dst_no_ltp = []
for idx, row in df_final.iterrows():
if row['LTP'] == 1:
dst_no_ltp.append(row['distances'])
dst_ltp.append(np.nan)
if row['LTP'] == 0:
dst_ltp.append(row['distances'])
dst_no_ltp.append(np.nan)
new_df = pd.DataFrame(columns=['No LTP at 6m', 'LTP at 6m'], index=range(0, len(dst_ltp)))
new_df['No LTP at 6m'] = dst_no_ltp
new_df['LTP at 6m'] = dst_ltp
df1 = new_df.transpose() # transpose the matrix
fig, ax = plt.subplots(figsize=(10,8))
df1.boxplot(column=['No LTP at 6m', 'LTP at 6m'])
和
import seaborn as sns
sns.boxplot(data=pd.melt(df1))
但是我还是得不到我想要的东西,应该是这样的:
不确定这是否正是您想要的。
如果您希望分解列表以便每个列表都有相应的 LTP
标签,那么这应该可行。
import itertools
def flatten(a):
return list(itertools.chain.from_iterable(a))
# flatten the list of lists
res = df.groupby('LTP')['distances'].apply(flatten).reset_index()
# explode lists
res = (res['distances'].apply(pd.Series)
.stack()
.reset_index(level=1, drop=True)
.to_frame('distances')).reset_index()
res.rename({'index':'LTP'}, axis=1, inplace=True)
# plot the new data
res.boxplot(column=['distances'], by='LTP', return_type='axes')
输出
此数据有问题:
修复数据:
- 使用
converters={'surface_distance': eval}
或 ast.literal_eval
将不起作用,因为所有数据行都没有在两端正确地用 []
括起来。
- 这是一个完整的解决方案,使用 Excel 文件中提供的数据。
surface_distance
的格式固定后,使用.explode()
,分隔列表
- 此函数适用于
pandas.Series
,因此将索引设置为 LTP
,因此每个 surface_distance
值都正确分配给其对应的 LTP
值。
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
def fix_my_data(x):
x = x.replace('[', '').replace(']', '').strip().split(',')
return [float(v.strip()) for v in x if v not in ['', ' ']]
# import the data
df = pd.read_excel('surface.xlsx')
# rename the column
df.rename(columns={'SurfaceDistances_Tumor2Ablation': 'surface_distance'}, inplace=True)
# create a valid list of numeric values
df.surface_distance = df.surface_distance.apply(fix_my_data)
# set index to LTP
df.set_index('LTP', inplace=True)
# pandas version 0.25 use explode to expand all lists
# update pandas if you're not on 0.25
df_sd = df.surface_distance.explode().rename_axis('LTP').reset_index(name='sd')
sns.boxplot(x='LTP', y='sd', data=df_sd)
plt.show()
LTP,surface_distances
1.0,[3. 2.4494898 3.1622777 0. 0. 0. 9 ]
1.0,"[ 9.530573 9.530573 9.161782 8.731052 9.770159 9.615404
9.480876 9.399235 9.371863 9.371863 9.371863 9.371863
8.422484 8.330477 8.206197 9.068727 8.925962 8.839198
8.54362 8.206197 7.919437 7.6890116 7.5201006 7.3780055
7.130104 6.4887457 5.864883 5.2647395 9.591869 9.457006
]"
1.0,[0. 0. 0. 3.6649203 3.870587 4.065864 ]
1.0,"[ 7.95181 8.283971 7.7533436 7.1679688 7.4169073 7.854291
8.127435 8.127435 7.619655 7.0959272 6.6085405 7.3821893
7.8215146 8.032048 8.032048 8.063969 7.619655 7.1679688
6.6498694 6.988916 7.3821893 7.8215146 8. 8.
8.032048 8.127435 7.7533436 7.3798757 6.6498694 6.988916
7.3821893 7.8215146 8. 8. 8.032048 8.127435
]"
用df = pd.read_clipboard(sep=',')
读取数据
还在数据中添加了link:https://drive.google.com/file/d/1vfplqd04zs9Bigp9Vq8y6HqwDALe00NY/view?usp=sharing
我想按 df[LTP]
对 df[distances]
列进行分组,其中只有代表 2 个类别的 0 和 1 值。
我试过:
df.boxplot(column=['distances'], by='LTP', ax=ax, return_type='axes')
AND 分离成一个单独的 2 列 DataFrame。
dst_ltp = []
dst_no_ltp = []
for idx, row in df_final.iterrows():
if row['LTP'] == 1:
dst_no_ltp.append(row['distances'])
dst_ltp.append(np.nan)
if row['LTP'] == 0:
dst_ltp.append(row['distances'])
dst_no_ltp.append(np.nan)
new_df = pd.DataFrame(columns=['No LTP at 6m', 'LTP at 6m'], index=range(0, len(dst_ltp)))
new_df['No LTP at 6m'] = dst_no_ltp
new_df['LTP at 6m'] = dst_ltp
df1 = new_df.transpose() # transpose the matrix
fig, ax = plt.subplots(figsize=(10,8))
df1.boxplot(column=['No LTP at 6m', 'LTP at 6m'])
和
import seaborn as sns
sns.boxplot(data=pd.melt(df1))
但是我还是得不到我想要的东西,应该是这样的:
不确定这是否正是您想要的。
如果您希望分解列表以便每个列表都有相应的 LTP
标签,那么这应该可行。
import itertools
def flatten(a):
return list(itertools.chain.from_iterable(a))
# flatten the list of lists
res = df.groupby('LTP')['distances'].apply(flatten).reset_index()
# explode lists
res = (res['distances'].apply(pd.Series)
.stack()
.reset_index(level=1, drop=True)
.to_frame('distances')).reset_index()
res.rename({'index':'LTP'}, axis=1, inplace=True)
# plot the new data
res.boxplot(column=['distances'], by='LTP', return_type='axes')
输出
此数据有问题:
修复数据:
- 使用
converters={'surface_distance': eval}
或ast.literal_eval
将不起作用,因为所有数据行都没有在两端正确地用[]
括起来。 - 这是一个完整的解决方案,使用 Excel 文件中提供的数据。
surface_distance
的格式固定后,使用.explode()
,分隔列表- 此函数适用于
pandas.Series
,因此将索引设置为LTP
,因此每个surface_distance
值都正确分配给其对应的LTP
值。
- 此函数适用于
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
def fix_my_data(x):
x = x.replace('[', '').replace(']', '').strip().split(',')
return [float(v.strip()) for v in x if v not in ['', ' ']]
# import the data
df = pd.read_excel('surface.xlsx')
# rename the column
df.rename(columns={'SurfaceDistances_Tumor2Ablation': 'surface_distance'}, inplace=True)
# create a valid list of numeric values
df.surface_distance = df.surface_distance.apply(fix_my_data)
# set index to LTP
df.set_index('LTP', inplace=True)
# pandas version 0.25 use explode to expand all lists
# update pandas if you're not on 0.25
df_sd = df.surface_distance.explode().rename_axis('LTP').reset_index(name='sd')
sns.boxplot(x='LTP', y='sd', data=df_sd)
plt.show()