Создать вычисляемый столбец во фрейме данных Pandas, содержащий отсортированные группы - PullRequest
0 голосов
/ 25 мая 2018

Предполагая, что существует кадр данных pandas со строками, содержащими некоторые сгруппированные данные, которые отсортированы (все группы значений для данного имени появляются рядом друг с другом), мы хотели бы ввести новый вычисляемый столбец, который назначаетзначения в зависимости от значений некоторого столбца.Если первое значение равно нулю, то все значения для группы получают первое ненулевое значение или nan, если такого значения нет.В противном случае, если первое значение ненулевое, назначается фиксированное значение, например -1.

Пример кадра входных данных:

   name    value
0     a        0
1     a        0
2     a        6
3     a        8
4     b        0
5     b        0
6     c        5
7     c        7

Пример кадра выходных данных сcalc создан столбец.

   name    value    calc
0     a        0       6
1     a        0       6      
2     a        6       6
3     a        8       6
4     b        0     nan
5     b        0     nan
6     c        5      -1
7     c        7      -1

Подход, о котором я думал, заключался в создании таблицы поиска первых ненулевых значений каждой группы, поэтому для приведенного выше примера это будет:

      value
 a        6
 c        5

Затем выполните итерацию фрейма входных данных и составьте список значений в соответствии с приведенной выше логикой, которая затем будет назначена новому столбцу.

Ответы [ 2 ]

0 голосов
/ 25 мая 2018

Это один из способов использования groupby.transform.Альтернативы в функции return_val непосредственно отражают 3 указанных вами условия и легко расширяются до других критериев.

def return_val(x):
    vals = x.values
    if 0 not in vals:
        return -1
    else:
        return next((i for i in vals if i!=0), np.nan)

df['calc'] = df.groupby('name')['value'].transform(return_val)

print(df)

  name  value  calc
0    a      0   6.0
1    a      0   6.0
2    a      6   6.0
3    a      8   6.0
4    b      0   NaN
5    b      0   NaN
6    c      5  -1.0
7    c      7  -1.0
0 голосов
/ 25 мая 2018

Для лучшей производительности лучше не использовать groupby, лучше создать окончательный словарь и map:

#get all names with 0
contains_zeros = df.loc[df['value'] == 0, 'name'].unique()
print (contains_zeros)
['a' 'b']

#get first non zero values only names with 0
s = df[df['name'].isin(contains_zeros) & (df['value'] != 0)].drop_duplicates('name')
print (s)
  name  value
2    a      6

#first non zero dictionary
d1 = s.set_index('name')['value'].to_dict()
print (d1)
{'a': 6}

#dictionary with all 0 in name
d2 = dict.fromkeys(set(contains_zeros) - set(s['name']), np.nan)
print (d2)
{'b': nan}

#all dictionary without 0
d3 = dict.fromkeys(set(df['name'].unique()) - set(contains_zeros), -1)
print (d3)
{'c': -1}

#merge all together
#https://stackoverflow.com/q/38987
d =  {**d1, **d2, **d3}
print (d)
{'a': 6, 'b': nan, 'c': -1}

df['calc'] = df['name'].map(d)
print (df)
  name  value  calc
0    a      0   6.0
1    a      0   6.0
2    a      6   6.0
3    a      8   6.0
4    b      0   NaN
5    b      0   NaN
6    c      5  -1.0
7    c      7  -1.0

Еще одно более медленное решение с groupby:

def f(x):
    if (x== 0).all():
        return np.nan
    elif (x == 0).any():
        return x[x != 0].iloc[0]
    else:
        return -1


df['calc'] = df.groupby('name')['value'].transform(f)
print (df)

  name  value  calc
0    a      0   6.0
1    a      0   6.0
2    a      6   6.0
3    a      8   6.0
4    b      0   NaN
5    b      0   NaN
6    c      5  -1.0
7    c      7  -1.0
...