Python 在宽数据框内将3个文本栏折叠为1个

Python 在宽数据框内将3个文本栏折叠为1个,python,pandas,dataframe,reduction,Python,Pandas,Dataframe,Reduction,我有一个数据集,其中一种数据类型分布在多个列中。我想把它们简化成一列。我有一个函数可以实现这一点,但这是一个繁琐的过程,我希望有一个更干净的方法来实现这一点。这是我的数据的一个玩具样本: UID COMPANY EML MAI TEL 273 7UP nan nan TEL 273 7UP nan MAI nan 906 WSJ nan nan TEL 906 WSJ

我有一个数据集,其中一种数据类型分布在多个列中。我想把它们简化成一列。我有一个函数可以实现这一点,但这是一个繁琐的过程,我希望有一个更干净的方法来实现这一点。这是我的数据的一个玩具样本:

UID    COMPANY    EML    MAI   TEL
273    7UP        nan    nan   TEL
273    7UP        nan    MAI   nan
906    WSJ        nan    nan   TEL
906    WSJ        EML    nan   nan
736    AIG        nan    MAI   nan
我想得到的是:

UID    COMPANY   CONTACT_INFO
273    7UP       MT
906    WSJ       ET
736    AIG       M
我通过编写一个函数来解决这个问题,该函数将
EML
MAI
TEL
转换为素数,聚合结果,然后将总和转换为组成触点类型。这是可行的,而且相当快。以下是一个示例:

def columnRedux(df):
    newDF = df.copy()
    newDF.fillna('-', inplace=True)
    newDF['CONTACT_INFO'] = newDF['EML'] + newDF['MAI'] + newDF['TEL']
    newDF.replace('EML--', 7, inplace=True)
    newDF.replace('-MAI-', 101, inplace=True)
    newDF.replace('--TEL', 1009, inplace=True)

    small = newDF.groupby(['UID', 'COMPANY'], as_index=False)['CONTACT_INFO'].sum()

    small.replace(7, 'E', inplace=True)
    small.replace(101, 'M', inplace=True)
    small.replace(108, 'EM', inplace=True)
    small.replace(1009, 'T', inplace=True)
    small.replace(1016, 'ET', inplace=True)
    small.replace(1110, 'MT', inplace=True)
    small.replace(1117, 'EMT', inplace=True)

    return small

df1 = pd.DataFrame(
    {'EML' : [np.nan, np.nan, np.nan, 'EML', np.nan, np.nan, 'EML', np.nan, np.nan, 'EML', 'EML', np.nan],
    'MAI' : [np.nan, 'MAI', np.nan, np.nan, 'MAI', np.nan, np.nan, np.nan, 'MAI', np.nan, np.nan, 'MAI'],
    'COMPANY' : ['7UP', '7UP', 'UPS', 'UPS', 'UPS', 'WSJ', 'WSJ', 'TJX', 'AIG', 'CDW', 'HEB', 'HEB'],
    'TEL' : ['TEL', np.nan, 'TEL', np.nan, np.nan, 'TEL', np.nan, 'TEL', np.nan, np.nan, np.nan, np.nan],
    'UID' : [273, 273, 865, 865, 865, 906, 906, 736, 316, 458, 531, 531]},
    columns=['UID', 'COMPANY', 'EML', 'MAI', 'TEL'])

cleanDF = columnRedux(df1)
我的问题是我有几个数据集,每个数据集都有自己的“宽”列集。有些需要减少5+列。对所有变体的转换进行硬编码并非易事。有没有更干净的方法来实现这一点?

也许不是“最好的”解决方案。但一种方法是使用简单的groupby并对包含的元素进行条件设置:

df = df.groupby(['UID','COMPANY'])[['EML','MAI','TEL']]\
    .apply(lambda x: ''.join(sorted([i[0] for y in x.values for i in y if pd.notnull(i)])))\
    .reset_index()\
    .rename(columns={0:'CONTACT_INFO'})
或者,另一种方法是将分组的数据帧转换为str类型,并替换字符串和sum。我想说,相当可读

m = {
    'nan':'',
    'EML':'E',
    'MAI':'M',
    'TEL':'T'
}

df = df.groupby(['UID','COMPANY'])[['EML','MAI','TEL']]\
       .apply(lambda x: x.astype(str).replace(m).sum().sum())\
       .reset_index()\
       .rename(columns={0:'CONTACT_INFO'})
完整示例:

import pandas as pd
import numpy as np

data = '''\
UID    COMPANY    EML    MAI   TEL
273    7UP        nan    nan   TEL
273    7UP        nan    MAI   nan
906    WSJ        nan    nan   TEL
906    WSJ        EML    nan   nan
736    AIG        nan    MAI   nan'''

fileobj = pd.compat.StringIO(data)
df = pd.read_csv(fileobj, sep='\s+').replace('NaN',np.nan)

# use a nested list comprehension to flatten the array and remove nans.
df = df.groupby(['UID','COMPANY'])[['EML','MAI','TEL']]\
    .apply(lambda x: ''.join(sorted([i[0] for y in x.values for i in y if pd.notnull(i)])))\
    .reset_index()\
    .rename(columns={0:'CONTACT_INFO'})

print(df)
返回:

UID  COMPANY  CONTACT_INFO
273      7UP            MT
736      AIG             M
906      WSJ            ET
dtype: object
也许不是“最好的”解决方案。但一种方法是使用简单的groupby并对包含的元素进行条件设置:

df = df.groupby(['UID','COMPANY'])[['EML','MAI','TEL']]\
    .apply(lambda x: ''.join(sorted([i[0] for y in x.values for i in y if pd.notnull(i)])))\
    .reset_index()\
    .rename(columns={0:'CONTACT_INFO'})
或者,另一种方法是将分组的数据帧转换为str类型,并替换字符串和sum。我想说,相当可读

m = {
    'nan':'',
    'EML':'E',
    'MAI':'M',
    'TEL':'T'
}

df = df.groupby(['UID','COMPANY'])[['EML','MAI','TEL']]\
       .apply(lambda x: x.astype(str).replace(m).sum().sum())\
       .reset_index()\
       .rename(columns={0:'CONTACT_INFO'})
完整示例:

import pandas as pd
import numpy as np

data = '''\
UID    COMPANY    EML    MAI   TEL
273    7UP        nan    nan   TEL
273    7UP        nan    MAI   nan
906    WSJ        nan    nan   TEL
906    WSJ        EML    nan   nan
736    AIG        nan    MAI   nan'''

fileobj = pd.compat.StringIO(data)
df = pd.read_csv(fileobj, sep='\s+').replace('NaN',np.nan)

# use a nested list comprehension to flatten the array and remove nans.
df = df.groupby(['UID','COMPANY'])[['EML','MAI','TEL']]\
    .apply(lambda x: ''.join(sorted([i[0] for y in x.values for i in y if pd.notnull(i)])))\
    .reset_index()\
    .rename(columns={0:'CONTACT_INFO'})

print(df)
返回:

UID  COMPANY  CONTACT_INFO
273      7UP            MT
736      AIG             M
906      WSJ            ET
dtype: object
让我们试试这个:

(df1.set_index(['UID','COMPANY']).notnull() * df1.columns[2:].str[0])\
.sum(level=[0,1]).sum(1).reset_index(name='CONTACT_INFO')
输出:

   UID COMPANY CONTACT_INFO
0  273     7UP           MT
1  865     UPS          EMT
2  906     WSJ           ET
3  736     TJX            T
4  316     AIG            M
5  458     CDW            E
6  531     HEB           EM
   UID COMPANY CONTACT_INFO
0  273     7UP           MT
1  865     UPS          EMT
2  906     WSJ           ET
3  736     TJX            T
4  316     AIG            M
5  458     CDW            E
6  531     HEB           EM
拆分为@AntonvBR:

df2 = df1.set_index(['UID','COMPANY'])
df_out  = ((df2.notnull() * df2.columns.str[0])
           .sum(level=[0,1]) #consolidate rows of contact info to one line
           .sum(1)  #sum across columns to create one column
           .reset_index(name='CONTACT_INFO'))
print(df_out)
输出:

   UID COMPANY CONTACT_INFO
0  273     7UP           MT
1  865     UPS          EMT
2  906     WSJ           ET
3  736     TJX            T
4  316     AIG            M
5  458     CDW            E
6  531     HEB           EM
   UID COMPANY CONTACT_INFO
0  273     7UP           MT
1  865     UPS          EMT
2  906     WSJ           ET
3  736     TJX            T
4  316     AIG            M
5  458     CDW            E
6  531     HEB           EM
让我们试试这个:

(df1.set_index(['UID','COMPANY']).notnull() * df1.columns[2:].str[0])\
.sum(level=[0,1]).sum(1).reset_index(name='CONTACT_INFO')
输出:

   UID COMPANY CONTACT_INFO
0  273     7UP           MT
1  865     UPS          EMT
2  906     WSJ           ET
3  736     TJX            T
4  316     AIG            M
5  458     CDW            E
6  531     HEB           EM
   UID COMPANY CONTACT_INFO
0  273     7UP           MT
1  865     UPS          EMT
2  906     WSJ           ET
3  736     TJX            T
4  316     AIG            M
5  458     CDW            E
6  531     HEB           EM
拆分为@AntonvBR:

df2 = df1.set_index(['UID','COMPANY'])
df_out  = ((df2.notnull() * df2.columns.str[0])
           .sum(level=[0,1]) #consolidate rows of contact info to one line
           .sum(1)  #sum across columns to create one column
           .reset_index(name='CONTACT_INFO'))
print(df_out)
输出:

   UID COMPANY CONTACT_INFO
0  273     7UP           MT
1  865     UPS          EMT
2  906     WSJ           ET
3  736     TJX            T
4  316     AIG            M
5  458     CDW            E
6  531     HEB           EM
   UID COMPANY CONTACT_INFO
0  273     7UP           MT
1  865     UPS          EMT
2  906     WSJ           ET
3  736     TJX            T
4  316     AIG            M
5  458     CDW            E
6  531     HEB           EM

通过使用
dot
groupby
第一列之后创建新列

s=df.groupby(['UID','COMPANY'],as_index=False).first()

s['CONTACT_INFO']=s[['EML','MAI','TEL']].notnull().dot(s.columns[2:].str[0])
s.dropna(1)
Out[349]: 
   UID COMPANY CONTACT_INFO
0  273     7UP           MT
1  736     AIG            M
2  906     WSJ           ET

通过使用
dot
groupby
第一列之后创建新列

s=df.groupby(['UID','COMPANY'],as_index=False).first()

s['CONTACT_INFO']=s[['EML','MAI','TEL']].notnull().dot(s.columns[2:].str[0])
s.dropna(1)
Out[349]: 
   UID COMPANY CONTACT_INFO
0  273     7UP           MT
1  736     AIG            M
2  906     WSJ           ET

好的,是的,现在我们在谈。不知道为什么要使用
s
,因为这意味着你有一个系列。dropna(1)不是100%稳定的。很好的解决方案。好的,是的,现在我们在谈。不知道为什么要使用
s
,因为这意味着你有一个系列。dropna(1)不是100%稳定的。很好的解决方案,这很好。我也不知道我可以用这种方式从字符串中干净地创建DF。谢谢你的帮助。我把这个作为答案。它解决了我所有的问题,我相信你是第一个回答的。@Lenwood是的,我可能是第一个。但我要说的是,你应该投票选出最适合你的候选人。其他解决方案也很聪明:)这很好。我也不知道我可以用这种方式从字符串中干净地创建DF。谢谢你的帮助。我把这个作为答案。它解决了我所有的问题,我相信你是第一个回答的。@Lenwood是的,我可能是第一个。但我要说的是,你应该投票选出最适合你的候选人。其他解决方案也相当聪明:)