9

I have a dataframe df:

data = {'id':[12,112],
        'idlist':[[1,5,7,12,112],[5,7,12,111,113]]
       }
df=pd.DataFrame.from_dict(data)

which looks like this:

    id                idlist
0   12    [1, 5, 7, 12, 112]
1  112  [5, 7, 12, 111, 113]

I need to check and see if id is in the idlist, and select or flag it. I have tried variations of the following and receive the commented error:

df=df.loc[df.id.isin(df.idlist),:] #TypeError: unhashable type: 'list'
df['flag']=df.where(df.idlist.isin(df.idlist),1,0) #TypeError: unhashable type: 'list'

Some possible other methods to a solution would be .apply in a list comprehension?

I am looking for a solution here that either selects the rows where id is in idlist, or flags the row with a 1 where id is in idlist. The resulting df should be either:

   id              idlist
0  12  [1, 5, 7, 12, 112]

or:

   flag   id                idlist
0     1   12    [1, 5, 7, 12, 112]
1     0  112  [5, 7, 12, 111, 113]

Thanks for the help!

Adam Jaamour
  • 1,326
  • 1
  • 15
  • 31
clg4
  • 2,863
  • 6
  • 27
  • 32

4 Answers4

18

Use apply:

df['flag'] = df.apply(lambda x: int(x['id'] in x['idlist']), axis=1)
print (df)
    id                idlist  flag
0   12    [1, 5, 7, 12, 112]     1
1  112  [5, 7, 12, 111, 113]     0

Similar:

df['flag'] = df.apply(lambda x: x['id'] in x['idlist'], axis=1).astype(int)
print (df)
    id                idlist  flag
0   12    [1, 5, 7, 12, 112]     1
1  112  [5, 7, 12, 111, 113]     0

With list comprehension:

df['flag'] = [int(x[0] in x[1]) for x in df[['id', 'idlist']].values.tolist()]
print (df)
    id                idlist  flag
0   12    [1, 5, 7, 12, 112]     1
1  112  [5, 7, 12, 111, 113]     0

Solutions for filtering:

df = df[df.apply(lambda x: x['id'] in x['idlist'], axis=1)]
print (df)
   id              idlist
0  12  [1, 5, 7, 12, 112]

df = df[[x[0] in x[1] for x in df[['id', 'idlist']].values.tolist()]]
print (df)

   id              idlist
0  12  [1, 5, 7, 12, 112]
jezrael
  • 822,522
  • 95
  • 1,334
  • 1,252
  • timeits on my df of 1.6m rows: – clg4 Nov 27 '17 at 16:11
  • I think list comprehension solution should be faster. – jezrael Nov 27 '17 at 16:17
  • 1
    here they are:timeits on my df of 1.6m rows: df['flag'] = df.loc[:, ('id', 'idlist')].apply(lambda x: 1 if x[0] in x[1] else 0, axis=1) 2min10s | df['flag'] = df.apply(lambda x: int(x['id'] in x['idlist']), axis=1) 1min55s | df['flag'] = df.apply(lambda x: x['id'] in x['idlist'], axis=1).astype(int) 1min54s | df['flag'] = [int(x[0] in x[1]) for x in df[['id', 'idlist']].values.tolist()] 1min24s | df.apply(lambda x : set([x.id]).issubset(x.idlist),1).astype(int). The various loops below and deleted were much slower. Winner is the list comprehension version from @jezreal! – clg4 Nov 27 '17 at 16:19
  • 2
    winner is the list comprehension based on my timeits – clg4 Nov 27 '17 at 16:26
4

You can use df.apply and process each row and create a new column flag that will check the condition and give you result as second output requested.

df['flag'] = df.loc[:, ('id', 'idlist')].apply(lambda x: 1 if x[0] in x[1] else 0, axis=1)

print(df)

where x[0] is id and x[1] is idlist

Aafaque Abdullah
  • 361
  • 3
  • 13
2

Try simple for loop:

flaglist = []
for i in range(len(df)):
    if df.id[i] in df.idlist[i]:
        flaglist.append(1)
    else:
        flaglist.append(0)
df["flag"] = flaglist 

df:

    id                idlist  flag
0   12    [1, 5, 7, 12, 112]     1
1  112  [5, 7, 12, 111, 113]     0

To drop rows:

flaglist = []
for i in range(len(df)):
    if df.id[i] not in df.idlist[i]:
        flaglist.append(i)
df = df.drop(flaglist)

df:

   id              idlist  flag
0  12  [1, 5, 7, 12, 112]     1

Above can be converted to list comprehension for creating a flag column:

df["flag"] = [df.id[i] in df.idlist[i]    for i in range(len(df))]
print(df)
#     id                idlist   flag
# 0   12    [1, 5, 7, 12, 112]   True
# 1  112  [5, 7, 12, 111, 113]  False

or

df["flag"] = [1 if df.id[i] in df.idlist[i] else 0    for i in range(len(df))]
print(df)
#     id                idlist  flag
# 0   12    [1, 5, 7, 12, 112]     1
# 1  112  [5, 7, 12, 111, 113]     0

and for selecting out rows:

flaglist = [i   for i in range(len(df))   if df.id[i] in df.idlist[i]]
print(df.iloc[flaglist])
#    id              idlist
# 0  12  [1, 5, 7, 12, 112]
rnso
  • 23,686
  • 25
  • 112
  • 234
1

By using issubset

df.apply(lambda  x : set([x.id]).issubset(x.idlist),1).astype(int)
Out[378]: 
0    1
1    0
dtype: int32

By using np.vectorize

def myfun(x,y):
    return np.in1d(x,y)


np.vectorize(myfun)(df.id,df.idlist).astype(int)

Timing :

%timeit np.vectorize(myfun)(df.id,df.idlist).astype(int)
10000 loops, best of 3: 92.3 µs per loop
%timeit df.apply(lambda  x : set([x.id]).issubset(x.idlist),1).astype(int)
1000 loops, best of 3: 353 µs per loop
BENY
  • 317,841
  • 20
  • 164
  • 234