如何在熊猫数据框中复制和修改行?

时间:2018-10-03 16:10:06

标签: python python-3.x pandas dataframe

我正在尝试使用txt文件中存储的大量数据来构造数据帧。但是,我没有构造数据,因此我必须使用其中包含的令人沮丧的格式。我无法使我的代码在大数据中运行(并且几乎使我的计算机崩溃了),因此设置一个较小的数据框,如下所示:

    'Value'             ID_1                ID_2
0   11122222            ABC42123            33333
1   21219299            YOF21233            88821
2   00022011            ERE00091            23124
3   75643311;21233332   ROB21288            99421
4   12412421            POW94277            12231;33221
5   54221721            IRS21231;YOU28137   13123

我的沮丧之处在于在数据中使用分号。该数据用于表示ID,但已将多个ID分配给多个变量。我想重复这些行,以便可以在数据中搜索各个ID,并拥有一个如下所示的数据表:

    'Value'             ID_1                ID_2
0   11122222            ABC42123            33333
1   21219299            YOF21233            88821
2   00022011            ERE00091            23124
3   75643311            ROB21288            99421
4   21233332            ROB21288            99421
5   12412421            POW94277            12231
6   12412421            POW94277            33221
7   54221721            YOU28137            13123
8   54221721            IRS21231            13123

重新索引不是问题,只要不同的ID彼此保持链接并保持其正确值即可。

不幸的是,到目前为止,我所有分割数据的尝试都以失败告终。我设法建立了一个函数,该函数可重复包含分号的数据,并通过我的函数为每一列进行解析,但随后无法拆分数据。

def delete_dup(df,column):
for a in column:
    location = df.loc[df.duplicated(subset= column, keep=False)]
    for x in location:
        semicolon = df.loc[df[column].str.contains(';', regex=True)]
        duplicate = semicolon.duplicated(subset= column, keep='first')
        tiny_df = semicolon.loc[duplicate]

        split_up = tiny_df[column].str.split(';')

        return pd.concat([df, split_up])



  'Value'              ID_1              ID_2           0
  11122222             ABC42123          33333          NaN
  21219299             YOF21233          88821          NaN
  00022011             ERE00091          23124          NaN
  75643311;21233332    ROB21288          99421          NaN
  12412421             POW94277          12231;33221    NaN
  54221721             IRS21231;YOU28137 13123          NaN
  75643311;21233332    ROB21288          99421          NaN
  54221721             IRS21231;YOU28137 13123          NaN
  12412421             POW94277          12231;33221    NaN
  NaN                  NaN               NaN            [75643311, 21233332]

我觉得这是我最近来过的地方,但距离我想要的地方还很遥远。我尝试在数据帧上执行的所有“ If”语句都遇到“ ValueError:DataFrame的真值不明确。请使用a.empty,a.bool(),a.item(),a.any()或a.all()。”错误,阅读起来实在令人沮丧。关于如何让熊猫做我想要的任何想法?

2 个答案:

答案 0 :(得分:0)

也许不是最优雅的方式,但是 可以解决问题:

第1步

我们拥有的数据:

<dependency>
                <groupId>org.apache.logging.log4j</groupId>
                <artifactId>log4j-api-java9</artifactId>
                <version>${log4j.version}</version>
    </dependency>

第2步

让我们拆分行为异常的列

df
    'Value'     ID_1        ID_2
0   11122222    ABC42123    33333
1   21219299    YOF21233    88821
2   00022011    ERE00091    23124
3   75643311;21233332   ROB21288    99421
4   12412421    POW94277    12231;33221
5   54221721    IRS21231;YOU28137   13123

第3步

让我们将重复数据和原始数据合并为一个数据帧:

df["'Value'_Dupe"] = df["'Value'"].apply(lambda x: x.split(";")[1] if len(x.split(";"))>1 else np.NaN)
df["'Value'"] = df["'Value'"].apply(lambda x: x.split(";")[0])
df["ID_1_Dupe"] = df["ID_1"].apply(lambda x: x.split(";")[1] if len(x.split(";"))>1 else np.NaN)
df["ID_1"] = df["ID_1"].apply(lambda x: x.split(";")[0])
df["ID_2_Dupe"] = df["ID_2"].apply(lambda x: x.split(";")[1] if len(x.split(";"))>1 else np.NaN)
df["ID_2"] = df["ID_2"].apply(lambda x: x.split(";")[0])
df

    'Value'     ID_1        ID_2    'Value'_Dupe    ID_1_Dupe   ID_2_Dupe
0   11122222    ABC42123    33333   NaN             NaN          NaN
1   21219299    YOF21233    88821   NaN             NaN          NaN
2   00022011    ERE00091    23124   NaN             NaN          NaN
3   75643311    ROB21288    99421   21233332        NaN          NaN
4   12412421    POW94277    12231   NaN             NaN          33221
5   54221721    IRS21231    13123   NaN             YOU28137     NaN

请告诉我这是否可以解决您的问题。

答案 1 :(得分:0)

解决方案分为两部分。第一个是识别哪些行带有分号,第二个是创建其他行并将其连接起来。第一部分在contains_sc中完成,第二部分通过遍历行并在检测到带有分号的行时运行函数create_additional_rows来完成。

希望这会有所帮助。

In[6]: import pandas as pd

In[7]: df = pd.DataFrame(
  [['1', '2;3', '4', '5'],
  ['A', 'B', 'C', 'D;E'],
  ['T', 'U', 'V;W', 'X']],
  index=['Val', 'ID1', 'ID2']
).T

In[8]: df

Out[8]: 
   Val  ID1  ID2
0    1    A    T
1  2;3    B    U
2    4    C  V;W
3    5  D;E    X

In[9]: contains_sc = df.apply(lambda x: x.str.contains(';'))
In[10]: contains_sc
Out[10]: 
     Val    ID1    ID2
0  False  False  False
1   True  False  False
2  False  False   True
3  False   True  False

In[11]: 
def create_additional_rows(data_row, csc_row, split_char=';'):
    """Given a duplicated row return additional de-duplicated rows."""
    if len(csc_row[csc_row].dropna()) > 1:
      raise ValueError('Expect only a single column with a semicolon')
    col_with_sc = csc_row[csc_row].dropna().index[0]
    retval = []
    for item in data_row.loc[col_with_sc].split(split_char):
      copied = data_row.copy()
      copied.loc[col_with_sc] = item
      retval.append(copied)
    return retval

In[11]: 
  new_rows = []
  for (idx, data_row), (_, csc_row) in zip(df.iterrows(), contains_sc.iterrows()):
    if True not in csc_row.values:
      new_rows.append(data_row)
      continue
    new_rows.extend(create_additional_rows(data_row, csc_row))

  final = pd.concat(new_rows, axis='columns').T.reset_index(drop=True)

In[13]: final
Out[13]: 
  Val ID1 ID2
0   1   A   T
1   2   B   U
2   3   B   U
3   4   C   V
4   4   C   W
5   5   D   X
6   5   E   X