Python 根据条件拆分为不同的行

Python 根据条件拆分为不同的行,python,pandas,grouping,Python,Pandas,Grouping,我有一个如下所示的数据框: data = [ [101, '1987-09-01', 1, 1, '1987-09-01', 2, 2], [102, '1987-09-01', 1, 1, '1999-09-01', 2, 2], [103, 'nan', 0, 0, '1999-09-01', 2, 2] ] df = pd.DataFrame(data, columns=['ID', 'Date1', 'x1', 'y1', 'Date2', 'x2', 'y2']

我有一个如下所示的数据框:

data = [
    [101, '1987-09-01', 1, 1, '1987-09-01', 2, 2],
    [102, '1987-09-01', 1, 1, '1999-09-01', 2, 2],
    [103, 'nan', 0, 0, '1999-09-01', 2, 2]
]
df = pd.DataFrame(data, columns=['ID', 'Date1', 'x1', 'y1', 'Date2', 'x2', 'y2'])
df['Date1'] = pd.to_datetime(df['Date1'])
df['Date2'] = pd.to_datetime(df['Date2'])
我的目标 如果日期列的值在一行中相同,则添加聚合x和y值。 如果它们不相同,请将行拆分为两行,并保持值不变

用(伪)代码解释:

另一个挑战是,包含日期的列可以少于或多于2列。但列名将始终包含字符串“Date”。例如,如果有三个具有三个不同值的不同日期列,则目标是创建三行。如果只有一个日期列,则无需进行任何修改

预期结果 用于先重塑形状,然后聚合
sum

df1 = pd.wide_to_long(df.reset_index(), 
                     stubnames=['Date','x','y'], 
                     i=['index','ID'], 
                     j='tmp')

df1 = df1.groupby(['index','ID','Date']).sum().reset_index(level=0, drop=True).reset_index()
print (df1)
    ID        Date  x  y
0  101  1987-09-01  3  3
1  102  1987-09-01  1  1
2  102  1999-09-01  2  2
3  103  1999-09-01  2  2
4  103         nan  0  0
如果
ID
值是唯一的,则应简化解决方案:

df1 = pd.wide_to_long(df, 
                     stubnames=['Date','x','y'], 
                     i='ID', 
                     j='tmp')

df1 = df1.groupby(['ID','Date']).sum().reset_index()
print (df1)
    ID        Date  x  y
0  101  1987-09-01  3  3
1  102  1987-09-01  1  1
2  102  1999-09-01  2  2
3  103  1999-09-01  2  2
4  103         nan  0  0
编辑:

如果列名称不以
1,2
结尾,则与日期列类似,您可以通过前两个字母对其进行规范化,然后应用上述解决方案(更改存根名称):

编辑2:我尝试创建更通用的解决方案:

data = [
    [101, '1987-09-01', 1, 1, '1987-09-01', 2, 2, 3],
    [102, '1987-09-01', 1, 1, '1999-09-01', 2, 2, 3],
    [103, 'nan', 0, 0, '1999-09-01', 2, 2, 3]
]
df = pd.DataFrame(data, columns=['ID', 'Date1', 'OPxx', 'NPxy', 'Date2',
                                 'OPyy', 'NPyx', 'WZ'])
df['Date1'] = pd.to_datetime(df['Date1'])
df['Date2'] = pd.to_datetime(df['Date2'])


s = df.columns.to_series()

#get first 2 characters 
s1 = s.str[:2]
#create groups starting by ID and Da (first 2 letters of Date)
s2 = s1.isin(['ID','Da']).cumsum().astype(str)

s = s1 + s2
print (s)
ID       ID1
Date1    Da2
OPxx     OP2
NPxy     NP2
Date2    Da3
OPyy     OP3
NPyx     NP3
WZ       WZ3
dtype: object

然后使用exclude
ID
index
创建子名称dynames-s1的所有唯一值:

print(np.setdiff1d(s1.unique(), ['ID', 'index']))
['Da' 'NP' 'OP' 'WZ']

df1 = pd.wide_to_long(df.reset_index(), 
                     stubnames=np.setdiff1d(s1.unique(), ['ID', 'index']), 
                     i=['index','ID1'], 
                     j='tmp')
合计金额:

df2 = (df1.groupby(['index','ID1','Da'])
          .sum()
          .reset_index(level=0, drop=True)
          .reset_index())
print (df2)
   ID1         Da  NP  OP   WZ
0  101 1987-09-01   3   3  3.0
1  102 1987-09-01   1   1  0.0
2  102 1999-09-01   2   2  3.0
3  103 1999-09-01   2   2  3.0

谢谢这确实满足了我的要求。然而,我的列名比我最初在示例中指出的要复杂一些。是否可以根据列字符串的前两个字符对列进行分组?请参阅编辑后的问题。@bjornvandijkman-不太容易,解决方案最好是将每个
OP
NP
标准化为
OP1,NP1
OP2,NP2
,就像第一个解决方案一样。您好,jaezrael。这个现在很好用。但是,每当引入一个以前从未见过的新列时,它就会中断(请参见我的编辑)。问题是,现在每当进行拆分时,该值都会添加到第一行,因为我猜分组是基于“stubnames”完成的。因此,我认为在找到带有日期的新列之前,为“Date”旁边的列指定相同的stubname是可行的。但是,我不知道如何实施这一点。有什么想法吗?提前非常感谢。我将为此创建一个新问题,并链接回此问题。
data = [
    [101, '1987-09-01', 1, 1, '1987-09-01', 2, 2, 3],
    [102, '1987-09-01', 1, 1, '1999-09-01', 2, 2, 3],
    [103, 'nan', 0, 0, '1999-09-01', 2, 2, 3]
]
df = pd.DataFrame(data, columns=['ID', 'Date1', 'OPxx', 'NPxy', 'Date2',
                                 'OPyy', 'NPyx', 'WZ'])
df['Date1'] = pd.to_datetime(df['Date1'])
df['Date2'] = pd.to_datetime(df['Date2'])


s = df.columns.to_series()

#get first 2 characters 
s1 = s.str[:2]
#create groups starting by ID and Da (first 2 letters of Date)
s2 = s1.isin(['ID','Da']).cumsum().astype(str)

s = s1 + s2
print (s)
ID       ID1
Date1    Da2
OPxx     OP2
NPxy     NP2
Date2    Da3
OPyy     OP3
NPyx     NP3
WZ       WZ3
dtype: object
df = df.rename(columns=s)
print (df)
   ID1        Da2  OP2  NP2        Da3  OP3  NP3  WZ3
0  101 1987-09-01    1    1 1987-09-01    2    2    3
1  102 1987-09-01    1    1 1999-09-01    2    2    3
2  103        NaT    0    0 1999-09-01    2    2    3
print(np.setdiff1d(s1.unique(), ['ID', 'index']))
['Da' 'NP' 'OP' 'WZ']

df1 = pd.wide_to_long(df.reset_index(), 
                     stubnames=np.setdiff1d(s1.unique(), ['ID', 'index']), 
                     i=['index','ID1'], 
                     j='tmp')
df2 = (df1.groupby(['index','ID1','Da'])
          .sum()
          .reset_index(level=0, drop=True)
          .reset_index())
print (df2)
   ID1         Da  NP  OP   WZ
0  101 1987-09-01   3   3  3.0
1  102 1987-09-01   1   1  0.0
2  102 1999-09-01   2   2  3.0
3  103 1999-09-01   2   2  3.0