Хотите выполнить группировку в Python, где сгруппированные данные будут поступать в строки - PullRequest
0 голосов
/ 30 ноября 2018

У меня есть такие данные:

ID Value
1  ABC
1  BCD
1  AKB
2  CAB
2  AIK
3  KIB

Я хочу выполнить операцию, которая даст мне что-то вроде этого:

ID Value1 Value2 Value3
1  ABC    BCD    AKB 
2  CAB    AIK
3  KIB

Я использовал SAS, где с помощью сохраненияи мы привыкли получать ответ.В Python я никак не получаю.Я знаю, что я должен использовать группу, а затем что-то.Но не знаю, что я могу использовать.В Pyspark, используя group by и collect_list, мы можем получить его в формате массива, но я хочу сделать это в Pandas dataframe

Ответы [ 3 ]

0 голосов
/ 30 ноября 2018

groupby + concat

Один из способов - выполнить итерацию объекта groupby и объединить результирующие кадры данных:

def group_gen(df):
    for key, x in df.groupby('ID'):
        x = x.set_index('ID').T
        x.index = pd.Index([key], name='ID')
        x.columns = [f'Value{i}' for i in range(1, x.shape[1]+1)]
        yield x

res = pd.concat(group_gen(df)).reset_index()

print(res)

   ID Value1 Value2 Value3
0   1    ABC    BCD    AKB
1   2    CAB    AIK    NaN
2   3    KIB    NaN    NaN
0 голосов
/ 30 ноября 2018

Используйте set_index с cumcount для MultiIndex, а затем измените форму на unstack:

df1 = (df.set_index(['ID',df.groupby('ID').cumcount()])['Value']
        .unstack()
        .rename(columns=lambda x: 'Value{}'.format(x + 1))
        .reset_index())

Для python 3.6+ возможно использовать f-string s для переименования имен столбцов:

df1 = (df.set_index(['ID',df.groupby('ID').cumcount()])['Value']
        .unstack()
        .rename(columns=lambda x: f'Value{x+1}')
        .reset_index())

Другая идея заключается в создании list s и новых DataFrame в конструкторе:

s = df.groupby('ID')['Value'].apply(list)
df1 = (pd.DataFrame(s.values.tolist(), index=s.index)
       .rename(columns=lambda x: 'Value{}'.format(x + 1))
       .reset_index())
print (df1)
   ID Value1 Value2 Value3
0   1    ABC    BCD    AKB
1   2    CAB    AIK    NaN
2   3    KIB    NaN    NaN

Производительность : Зависит от количества строк и количества уникальных значений столбца ID:

np.random.seed(45)

a = np.sort(np.random.randint(1000, size=10000))
b = np.random.choice(list('abcde'), size=10000)

df = pd.DataFrame({'ID':a, 'Value':b})
#print (df)

In [26]: %%timeit
    ...: (df.set_index(['ID',df.groupby('ID').cumcount()])['Value']
    ...:         .unstack()
    ...:         .rename(columns=lambda x: f'Value{x+1}')
    ...:         .reset_index())
    ...: 
8.96 ms ± 628 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

In [27]: %%timeit
    ...: s = df.groupby('ID')['Value'].apply(list)
    ...: (pd.DataFrame(s.values.tolist(), index=s.index)
    ...:        .rename(columns=lambda x: 'Value{}'.format(x + 1))
    ...:        .reset_index())
    ...: 
    ...: 
105 ms ± 7.39 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

#jpp solution
In [28]: %%timeit
    ...: def group_gen(df):
    ...:     for key, x in df.groupby('ID'):
    ...:         x = x.set_index('ID').T
    ...:         x.index = pd.Index([key], name='ID')
    ...:         x.columns = [f'Value{i}' for i in range(1, x.shape[1]+1)]
    ...:         yield x
    ...: 
    ...: pd.concat(group_gen(df)).reset_index()
    ...: 

3.23 s ± 20.1 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
0 голосов
/ 30 ноября 2018

Предполагая, что ваши данные находятся в кадре данных df, вы должны сделать это:

from pyspark.sql.functions import *

result = df.groupBy(col('ID')).agg(collect_list(col('Value')).alias('Values'))

how = result.select(max(size(col('Values'))).alias('len')).collect()

for i in range(how[0]['len']):
    result = result.withColumn('Values' + str(i+1), col('Values')[i])

Тогда результат будет выглядеть так:

ID    Values1    Values2    Values3
1     ABC        BCD        AKB
2     CAB        AIK
3     KIB
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...