Как удалить строку панды на основе значений столбца массива? - PullRequest
0 голосов
/ 26 апреля 2019

У меня есть следующий фрейм данных в пандах:

     id              name categoryids    shops
5   239         Boulanger         [5]      152
3   196  Bouygues Telecom         [5]      500
4   122             Darty       [5,3]      363
1   311     Electro Dépôt         [5]       81
0  2336            Orange        [15]      578
2   194            Orange         [5]      577

Я хотел бы отбросить 5-ю строку, потому что она дублируется по имени, но имеет другое значение в столбце categoryids , нотак как значения являются массивами (поскольку они могут иметь более одного значения), у меня возникла проблема при их сравнении.

Моя идея состояла в том, чтобы проверить режим этого столбца и отбросить все строки, которые не имеют этого значения вего массив (например, в этом случае режим будет 5, поэтому 5-й столбец следует отбросить, так как это значение отсутствует в его массиве), но у меня возникают проблемы с вычислением этого значения, поскольку столбец является массивом, а неодно значение.

Любые идеи или предложения о том, как это сделать?

Я использую Python 3.7 и последнюю версию панд.

Спасибо.

Ответы [ 4 ]

3 голосов
/ 26 апреля 2019

Сначала мы можем отметить, какие строки в столбце name являются дубликатами.

Тогда мы можем unnest ваши массивы в categoryids с помощью функции, найденной в этом ответе.

Наконец, мы фильтруем, для каких строк не помечены как дубликаты или равны mode:

def unnest(df, tile, explode):
    vals = df[explode].sum(1)
    rs = [len(r) for r in vals]
    a = np.repeat(df[tile].values, rs, axis=0)
    b = np.concatenate(vals.values)
    d = np.column_stack((a, b))
    return pd.DataFrame(d, columns = tile +  ['_'.join(explode)])

# Mark duplicate rows
df['dups'] = df.name.duplicated(keep=False).astype(int)
# Unnest categoryids column
df2 = unnest(df, ['id', 'name', 'shops', 'dups'], ['categoryids'])

print(df2)
     id              name shops dups categoryids
0   239         Boulanger   152    0           5
1   196  Bouygues Telecom   500    0           5
2   122             Darty   363    0           5
3   122             Darty   363    0           3
4   311     Electro Dépôt    81    0           5
5  2336            Orange   578    1          15
6   194            Orange   577    1           5

Отфильтровать дублирующиеся строки, которые не равны режиму:

mode = df2['categoryids'].mode()

df2 = df2[~df2['dups'].eq(1) | df2['categoryids'].isin(mode)].drop('dups', axis=1)

print(df2)
    id              name shops categoryids
0  239         Boulanger   152           5
1  196  Bouygues Telecom   500           5
2  122             Darty   363           5
3  122             Darty   363           3
4  311     Electro Dépôt    81           5
6  194            Orange   577           5

По желанию Мы можем группировать по name, чтобы вернуть массивы:


df2 = df2.groupby('name').agg({'id':'first',
                               'shops':'first',
                              'categoryids':list}).reset_index()

print(df2)
               name   id  shops categoryids
0         Boulanger  239    152         [5]
1  Bouygues Telecom  196    500         [5]
2             Darty  122    363      [5, 3]
3     Electro Dépôt  311     81         [5]
4            Orange  194    577         [5]
1 голос
/ 26 апреля 2019

С таким фреймом данных:

df = pd.DataFrame({'id': [239,196,122,311,2336,194,],
'name': ['Boulanger','Bouygues Telecom','Darty','Electro Dépôt','Orange','Orange',],
'shops': [152, 500, 363, 81, 578, 577,],
'categoryids': [[5],[5],[5,3],[5],[15],[5],]})

Вы можете сделать:

df.sort_values('categoryids').drop_duplicates('name', keep='first')

Сортирует столбец categoryids, затем удаляет дубликаты в name и сохраняет первый из них.

EDIT:

Еще одна вещь, которую вы можете сделать, это проверить, существует ли значение, которое вы указали в столбце categoryids:

df["exist"] = [int(5 in r)  for r in df["categoryids"]]

Что даст вам:

    id              name                shops   categoryids exist
0   239             Boulanger             152            [5]    1
1   196             Bouygues Telecom      500            [5]    1
2   122             Darty                 363         [5, 3]    1
3   311             Electro Dépôt          81            [5]    1
4   2336            Orange                578           [15]    0
5   194             Orange                577            [5]    1

И тогда вы можете взять только те, которые существуют:

df[df['exist'] == 1]

Затем найдите дубликаты, используя pd.duplicated(), как упомянуто @Erfan:

df['dups'] = df['name'].duplicated(keep=False).astype(int)


    id  name               shops     categoryids    exist   dups
0   239 Boulanger            152             [5]    1          0
1   196 Bouygues Telecom     500             [5]    1          0
2   122 Darty                363          [5, 3]    1          0
3   311 Electro Dépôt         81             [5]    1          0
4   2336    Orange           578            [15]    0          1
5   194 Orange               577             [5]    1          1


df[(
    (df['dups']!=1) | 
    (df['exist']!=0)
)].drop(['exist', 'dups'], axis=1).reset_index()

Результат:

index   id  name               shops    categoryids
0   0   239 Boulanger            152    [5]
1   1   196 Bouygues Telecom     500    [5]
2   2   122 Darty                363    [5, 3]
3   3   311 Electro Dépôt         81    [5]
4   5   194 Orange               577    [5]
0 голосов
/ 26 апреля 2019

Мне удалось сделать это, используя ответ @VnC, с некоторыми изменениями, так как я думал, что массивы categoryids , где фактические массивы целых чисел (как в примере выше), но я обнаружил, что они были строки (не массивы строк, а простые строки):

retailersIds_df = get_dataframe() # external method to get the dataframe, not relevant
retailersIds_df['categoryids'] = retailersIds_df['categoryids'].str.replace('[', '')
retailersIds_df['categoryids'] = retailersIds_df['categoryids'].str.replace(']', '')
retailersIds_df['categoryids'] = retailersIds_df['categoryids'].str.split(',')
# the following lines are used to calculate the mode of all the values contained in the arrays. 

ids_aux = []
for row in retailersIds_df.itertuples():
    ids_aux = ids_aux + row.categoryids
mydict = Counter(ids_aux)
mode = [key for key, value in mydict.items() if value ==  max(mydict.values())][0] 

# the counter module returns a dict, and the key (the actual value) of the most repeated value is chosen.
#the [0] is for the case where two keys have the same value, and the first is chosen (arbitrarily)

retailersIds_df["exist"] = [int(mode in r) for r in retailersIds_df["categoryids"]]
retailersIds_df = retailersIds_df[retailersIds_df['exist'] == 1]

Цикл сделан для расчета режима, хотя, вероятно, существует лучшая опция (я знаю, что циклы не следует делать в кадре данных панд, но я не мог придумать другой вариант, учитывая, что массивы могут быть произвольная длина)

0 голосов
/ 26 апреля 2019

Вы можете попробовать:

df = df.drop_duplicates(subset = ['name'])

Это будет смотреть на дубликаты только в имени столбца.Вы можете комбинировать столбцы, добавляя другие имена столбцов в список подмножеств.

...