Панды группового с логическим ИЛИ - PullRequest
0 голосов
/ 03 января 2019

Я хотел бы создать набор групп на основе логического критерия ИЛИ в пандах. Группа состоит из членов, соответствующих столбцу A или столбцу B.

Например, в этом фрейме данных:

df = pd.DataFrame([[1,1],[2,2],[2,3],[2,4],[3,3],[4,5]], columns = ['A','B'])

   A  B
0  1  1
1  2  2
2  2  3
3  2  4
4  3  3
5  4  5

Поскольку строки 1, 2 и 3 совпадают в столбце A, а строки 2 и 4 в столбце B, я хотел бы, чтобы значения идентификаторов были:

   A  B  id
0  1  1  0
1  2  2  1
2  2  3  1
3  2  4  1
4  3  3  1
5  4  5  2

Я не могу найти никакого решения, кроме создания NxN-графика с подключениями и использования scipy.sparse.csgraph.connected_components. Есть ли более простые варианты?

Ответы [ 3 ]

0 голосов
/ 03 января 2019

Мы можем сделать это, используя класс Counter. Мы подсчитываем количество вхождений для каждого элемента в столбце и создаем временный столбец с этими значениями. Если значение для строки в этом временном столбце больше 1 (что означает, что число встречается более одного раза, мы изменяем столбец id.

import pandas as pd
from collections import Counter as ctr
df = pd.DataFrame([[1,1],[2,2],[2,3],[2,4],[3,3],[4,5]], columns = ['A','B'])
df['id'] = 0
for i in range(len(df.columns)):
  if list(df.columns)[i] != 'id':
    c = dict(ctr(df[list(df.columns)[i]]))
    df[list(df.columns)[i] + '_1'] = df[list(df.columns)[i]].apply(lambda x: c[x])
    df.loc[df[list(df.columns)[i] + '_1'] > 1, 'id'] = 1
    df = df.drop(columns=[list(df.columns)[i] + '_1'])

df
   A  B  id
0  1  1   0
1  2  2   1
2  2  3   1
3  2  4   1
4  3  3   1
5  4  5   0

Этот должен быть масштабируемым для> 2 столбцов.

0 голосов
/ 15 января 2019

Спасибо @ W-B за то, что поставили меня на правильные позиции. Вот более общий ответ, который подходит для> 2 столбцов и где значения не связаны между столбцами.

import pandas as pd
import networkx as nx
from itertools import chain, combinations

columns = ['A','B','C']
df = pd.DataFrame([[1,1,1],[2,2,2],[2,3,3],[2,4,4],[3,3,4],[4,5,5]], columns = columns)
# make columns unique, so that values in any column are not treated as equivalent to values in another
# if you don't want to overwrite values, create new columns instead
for col in df.columns:
    df[col] = str(col)+df[col].astype(str)

colPairs = list(combinations(columns, 2)) # we could match on a subset of column pairs instead
G = nx.compose_all([nx.from_pandas_edgelist(df, colPair[0], colPair[1]) for colPair in colPairs])
l=list(nx.connected_components(G))
l=[dict.fromkeys(y,x)for x,y in enumerate(l)]
d=dict(chain(*map(dict.items,l)))
df['ID']=df.B.map(d)

print(df)

    A   B   C  ID
0  A1  B1  C1   0
1  A2  B2  C2   1
2  A2  B3  C3   1
3  A2  B4  C4   1
4  A3  B3  C4   1
5  A4  B5  C5   2
0 голосов
/ 03 января 2019

Обратите внимание, я думаю, что это проблема с сетью, поэтому мы делаем с networkx

import networkx as nx
G=nx.from_pandas_edgelist(df, 'A', 'B')
l=list(nx.connected_components(G))
l
[{1}, {2, 3}]

from itertools import chain
l=[dict.fromkeys(y,x)for x,y in enumerate(l)]#create the list of dict for later map 
d=dict(chain(*map(dict.items,l)))# flatten the list of dict to one dict 

df['ID']=df.B.map(d)

df
   A  B  ID
0  1  1   0
1  2  2   1
2  2  3   1
3  3  3   1

Обновление

s1=df.A.astype('category').cat.codes.sort_values()

s2=df.B.astype('category').cat.codes.sort_values()

s=((s1==s1.shift())|(s2==s2.shift())).eq(False).cumsum()
s
#df['new']=s
Out[25]:
0    1
1    2
2    2
3    2
4    2
5    3
dtype: int32+
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...