我有一个熊猫数据框,格式为:
df
ID col_1 col_2 col_3 Date
1 20 40 1/1/2018
1 10 1/2/2018
1 50 60 1/3/2018
3 40 10 90 1/1/2018
4 80 80 1/1/2018
问题是,我需要为每个列创建一个具有第一个有效值的新数据框,但还要从“日期”派生其他列,这与在原始数据框中匹配这些值的时间相对应。
换句话说:
new_df
ID first_col_1 Date_col_1 first_col_2 Date_col_2 first_col_3 Date_col_3
1 10 1/2/2018 20 1/1/2018 40 1/1/2018
3 40 1/1/2018 10 1/1/2018 90 1/1/2018
4 1/1/2018 80 1/1/2018 80 1/1/2018
我知道获得每个ID的每列第一个有效值很简单
df.groupby('ID').first()
但是如何为每一列提取相关的“日期”信息?
答案 0 :(得分:1)
IIUC在melt
之前使用groupby
newdf=df.melt(['ID','Date']).loc[lambda x : x.value!='']
newdf= newdf.groupby(['ID','variable']).first().unstack().sort_index(level=1,axis=1)
newdf.columns=newdf.columns.map('_'.join)
newdf
Date_col_1 value_col_1 Date_col_2 value_col_2 Date_col_3 value_col_3
ID
1 1/2/2018 10.0 1/1/2018 20.0 1/1/2018 40.0
3 1/1/2018 40.0 1/1/2018 10.0 1/1/2018 90.0
4 None NaN 1/1/2018 80.0 1/1/2018 80.0
答案 1 :(得分:1)
您不需要循环,但是在进行分组操作之前,您需要“融化”数据框。
所以从以下开始:
from io import StringIO
import pandas
f = StringIO("""\
ID,col_1,col_2,col_3,Date
1,,20,40,1/1/2018
1,10,,,1/2/2018
1,50,,60,1/3/2018
3,40,10,90,1/1/2018
4,,80,80,1/1/2018
""")
df = pandas.read_csv(f)
然后,您可以:
print(
df.melt(id_vars=['ID', 'Date'], value_vars=['col_1', 'col_2', 'col_3'], value_name='first')
.groupby(by=['ID', 'variable'])
.first()
.unstack(level='variable')
)
哪个给你:
Date first
variable col_1 col_2 col_3 col_1 col_2 col_3
ID
1 1/1/2018 1/1/2018 1/1/2018 10.0 20.0 40.0
3 1/1/2018 1/1/2018 1/1/2018 40.0 10.0 90.0
4 1/1/2018 1/1/2018 1/1/2018 NaN 80.0 80.0
这些列是多层的,因此您可以根据需要对它们进行一些修饰:
def flatten_columns(df, sep='_'):
newcols = [sep.join(_) for _ in df.columns]
return df.set_axis(newcols, axis='columns', inplace=False)
print(
df.melt(id_vars=['ID', 'Date'], value_vars=['col_1', 'col_2', 'col_3'], value_name='first')
.groupby(by=['ID', 'variable'])
.first()
.unstack(level='variable')
.sort_index(level='variable', axis='columns')
.pipe(flatten_columns)
)
这为您提供了一些与示例不完全相同的列顺序,但是与我想的差不多。
Date_col_1 first_col_1 Date_col_2 first_col_2 Date_col_3 first_col_3
ID
1 1/1/2018 10.0 1/1/2018 20.0 1/1/2018 40.0
3 1/1/2018 40.0 1/1/2018 10.0 1/1/2018 90.0
4 1/1/2018 NaN 1/1/2018 80.0 1/1/2018 80.0
答案 2 :(得分:0)
我认为您必须遍历各列,并在连接之前为它们中的每一个提取第一个值。我看不到一种更简单的方法。
# Create a list to store the dataframes you want for each column
sub_df = [pd.DataFrame(df['ID'].unique(), columns=['ID'])] # Init this list with IDs
for col in df.columns[1:-1]: # loop over the columns (except ID and Date)
# Determine the first valid rows indexes for this column (group by ID)
valid_rows = df.groupby('ID')[col].apply(lambda sub_df: sub_df.first_valid_index())
# Extracting the values and dates corresponding to these rows
new_sub_df = df[[col, 'Date']].ix[valid_rows].reset_index(drop=True)
# Append to the list of sub DataFrames
sub_df.append(new_sub_df)
# Concatenate all these DataFrames.
new_df = pd.concat(sub_df, axis=1)