【问题标题】:Combine rows (under a condition) in a Pandas Dataframe在 Pandas Dataframe 中组合行(在某个条件下)
【发布时间】:2014-09-19 03:39:05
【问题描述】:

我有一个数据框 (df)(最初来自一个 excel 文件),前 9 行是这样的:

      Control      Recd_Date/Due_Date                Action        Signature/Requester
0     2000-1703   2000-01-31 00:00:00           OC/OER/OPA/PMS/                 M WEBB
1           NaN   2000-02-29 00:00:00                       NaN              DATA CORP
2     2000-1776   2000-01-02 00:00:00            OC/ORA/OE/DCP/                  G KAN
3           NaN   2000-01-03 00:00:00           OC/ORA/ORO/PNC/              PALM POST
4           NaN                   NaN  FDA/OGROP/ORA/SE-FO/FLA-                    NaN
5           NaN                   NaN                DO/FLA-CB/                    NaN
6     2000-1983   2000-02-02 00:00:00  FDA/OGROP/ORA/CE-FO/CHI-                 M EGAN
7           NaN   2000-02-03 00:00:00                DO/CHI-CB/   BERNSTEIN LIEBHARD &
8           NaN                   NaN                       NaN             LONDON LLP
  • Type(df['Control'][1])=float;
  • Type(df['Recd_Date/Due_Date'][1])=datetime.datetime;
  • type(df['Action_Office'][1])=float;
  • Type(df['Signature/Requester'][1])=unicode

我想将此数据框(例如前 9 行)转换为:

      Control            Recd_Date/Due_Date                           Action                                                            Signature/Requester
0     2000-1703   2000-01-31 00:00:00,2000-02-29 00:00:00           OC/OER/OPA/PMS/                                                      M WEBB,DATA CORP
1     2000-1776   2000-01-02 00:00:00,2000-01-03 00:00:00           OC/ORA/OE/DCP/OC/ORA/ORO/PNC/FDA/OGROP/ORA/SE-FO/FLA-DO/FLA-CB/      G KAN,PALM POST
2     2000-1983   2000-02-02 00:00:00,2000-02-03 00:00:00           FDA/OGROP/ORA/CE-FO/CHI-DO/CHI-CB/                                   M EGAN,BERNSTEIN LIEBHARD & LONDON LLP

所以基本上:

  • 每次 pd.isnull(row['Control'])(这应该是唯一的 if 条件)为真时,然后将此行与前一行(其 'control' 值不为空)合并。
  • 对于“Recd_Date/Due_Date”和“Signature/Requester”,在每两个值(来自两个合并的行)之间添加“,”(或“/”)(例如“2000-01-31 00:00:00” ,2000-02-29 00:00:00' 和 'G KAN,PALM POST')
  • 对于“操作”,只需合并它们而不添加任何标点符号(例如 FDA/OGROP/ORA/CE-FO/CHI-DO/CHI-CB/)

有人可以帮我吗?这是我试图让它工作的代码:

for i, row in df.iterrows():
    if pd.isnull(df.ix[i]['Control_#']):
       df.ix[i-1]['Recd_Date/Due_Date'] = str(df.ix[i-1]['Recd_Date/Due_Date'])+'/'+str(df.ix[i]['Recd_Date/Due_Date'])
       df.ix[i-1]['Subject'] = str(df.ix[i-1]['Subject'])+' '+str(df.ix[i]['Subject'])
       if str(df.ix[i-1]['Action_Office'])[-1] == '-':
           df.ix[i-1]['Action_Office'] = str(df.ix[i-1]['Action_Office'])+str(df.ix[i]['Action_Office'])
       else:
           df.ix[i-1]['Action_Office'] = str(df.ix[i-1]['Action_Office'])+','+str(df.ix[i]['Action_Office'])
       if pd.isnull(df.ix[i-1]['Signature/Requester']):
           df.ix[i-1]['Signature/Requester'] = str(df.ix[i-1]['Signature/Requester'])+str(df.ix[i]['Signature/Requester'])
       elif str(df.ix[i-1]['Signature/Requester'])[-1] == '&':
           df.ix[i-1]['Signature/Requester'] = str(df.ix[i-1]['Signature/Requester'])+' '+str(df.ix[i]['Signature/Requester'])
       else:
           df.ix[i-1]['Signature/Requester'] = str(df.ix[i-1]['Signature/Requester'])+','+str(df.ix[i]['Signature/Requester'])
       df.drop(df.index[i])

为什么 drop() 不起作用?我正在尝试删除当前行(如果其 ['Control_#'] 为空),因此可以将下一行(其 ['Control_#'] 为空)添加到上一行(其 ['Control_#'] 为NOT null) 迭代..

非常感谢!!

【问题讨论】:

标签: python pandas data-cleaning


【解决方案1】:

我认为您需要将行分组在一起,然后将列值连接起来。棘手的部分是找到一种方法以您想要的方式将行组合在一起。这是我的解决方案...

1) 将行分组在一起:静态变量

由于您的组取决于行中的序列,因此我在方法中使用静态变量将每一行标记到特定组

def rolling_group(val):
    if pd.notnull(val): rolling_group.group +=1 #pd.notnull is signal to switch group
    return rolling_group.group
rolling_group.group = 0 #static variable

此方法沿 Control 系列应用以将索引排序为组,然后用于拆分数据框以允许您合并行

#groups = df.groupby(df['Control'].apply(rolling_group),as_index=False)

这确实是唯一棘手的部分,您可以通过对每个组应用一个函数来合并行,从而为您提供所需的输出

完整的解决方案代码

def rolling_group(val):
    if pd.notnull(val): rolling_group.group +=1 #pd.notnull is signal to switch group
    return rolling_group.group
rolling_group.group = 0 #static variable

def joinFunc(g,column):
    col =g[column]
    joiner = "/" if column == "Action" else ","
    s = joiner.join([str(each) for each in col if pd.notnull(each)])
    s = re.sub("(?<=&)"+joiner," ",s) #joiner = " "
    s = re.sub("(?<=-)"+joiner,"",s) #joiner = ""
    s = re.sub(joiner*2,joiner,s)    #fixes double joiner condition
    return s

#edit 上面 - str(each) - 转换为字符串... 编辑上面的正则表达式以清除连接字符串连接

if __name__ == "__main__":
    df = """      Control      Recd_Date/Due_Date                Action        Signature/Requester
0     2000-1703   2000-01-31 00:00:00           OC/OER/OPA/PMS/                 M WEBB
1           NaN   2000-02-29 00:00:00                       NaN              DATA CORP
2     2000-1776   2000-01-02 00:00:00            OC/ORA/OE/DCP/                  G KAN
3           NaN   2000-01-03 00:00:00           OC/ORA/ORO/PNC/              PALM POST
4           NaN                   NaN  FDA/OGROP/ORA/SE-FO/FLA-                    NaN
5           NaN                   NaN                DO/FLA-CB/                    NaN
6     2000-1983   2000-02-02 00:00:00  FDA/OGROP/ORA/CE-FO/CHI-                 M EGAN
7           NaN   2000-02-03 00:00:00                DO/CHI-CB/   BERNSTEIN LIEBHARD &
8           NaN                   NaN                       NaN             LONDON LLP"""
    df =  pd.read_csv(StringIO.StringIO(df),sep = "\s\s+",engine='python')

    groups = df.groupby(df['Control'].apply(rolling_group),as_index=False)
    groupFunct = lambda g: pd.Series([joinFunc(g,col) for col in g.columns],index=g.columns)
    print groups.apply(groupFunct)

输出

     Control                       Recd_Date/Due_Date  \
0  2000-1703  2000-01-31 00:00:00,2000-02-29 00:00:00   
1  2000-1776  2000-01-02 00:00:00,2000-01-03 00:00:00   
2  2000-1983  2000-02-02 00:00:00,2000-02-03 00:00:00   

                                              Action  \
0                                    OC/OER/OPA/PMS/   
1  OC/ORA/OE/DCP/OC/ORA/ORO/PNC/FDA/OGROP/ORA/SE-...   
2                 FDA/OGROP/ORA/CE-FO/CHI-DO/CHI-CB/   

                      Signature/Requester  
0                        M WEBB,DATA CORP  
1                         G KAN,PALM POST  
2  M EGAN,BERNSTEIN LIEBHARD & LONDON LLP  

【讨论】:

  • 哦,非常感谢!!在我的原始数据框中,type(df['Recd_Date/Due_Date'][i]) = datetime.datetime.因此,当我在我的数据帧上运行您的代码时,我得到“TypeError: sequence item 1: expected string or Unicode, datetime.datetime found”...我将 ['Recd_Date/Due_Date'] 列转换为 str as 'df[' Recd_Date/Due_Date'] = df['Recd_Date/Due_Date'].apply(str)' 但仍然得到相同的错误。为什么?谢谢..
  • 我相信这是因为 string.join(iterable) 方法只接受带有字符串的迭代。我编辑了 joinFunction 方法以将所有项目转换为它的列表理解部分中的字符串。您的其他列也使用非字符串/Unicode 类型。另一个提示是您可以使用 astype 来转换列数据类型
  • 我添加了一些额外的正则表达式来清理连接,你明白代码中发生了什么吗?
  • 我使用 re.sub 行在每个条件下将“-/”之类的内容更改为“-”/。代码应该与 NaT 一起工作。想要发布数据框的创建,以便我可以使用您正在使用的 dtypes? df.to_dict('records')[:10]
  • 嗨,所以我刚刚使用了你的 dtypes,我发布的所有代码都在工作。至于我是如何从列表中删除空值的,请看这段代码...[str(each) for each in col if pd.notnull(each)] 这是一个列表理解,最后有一个 if 语句删除空值。该代码从您的列中返回所有非空值,该列被转换为另一个列表中的字符串。
猜你喜欢
  • 1970-01-01
  • 2018-01-17
  • 2018-11-25
  • 1970-01-01
  • 2017-08-06
  • 2018-04-04
  • 2012-06-13
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多