I would use the following generic function which explodes lists in columns into rows:
def explode(df, lst_cols, fill_value=''):
# make sure `lst_cols` is a list
if lst_cols and not isinstance(lst_cols, list):
lst_cols = [lst_cols]
# all columns except `lst_cols`
idx_cols = df.columns.difference(lst_cols)
# calculate lengths of lists
lens = df[lst_cols[0]].str.len()
if (lens > 0).all():
# ALL lists in cells aren't empty
return pd.DataFrame({
col:np.repeat(df[col].values, df[lst_cols[0]].str.len())
for col in idx_cols
}).assign(**{col:np.concatenate(df[col].values) for col in lst_cols}) \
.loc[:, df.columns]
else:
# at least one list in cells is empty
return pd.DataFrame({
col:np.repeat(df[col].values, df[lst_cols[0]].str.len())
for col in idx_cols
}).assign(**{col:np.concatenate(df[col].values) for col in lst_cols}) \
.append(df.loc[lens==0, idx_cols]).fillna(fill_value) \
.loc[:, df.columns]
Demo:
In [88]: df
Out[88]:
User Category Rating
0 1 [1, 2, 3] [5, 1, 3]
1 2 [3, 2, 1] [3, 1, 1]
2 3 [1, 3, 1] [2, 1, 4]
In [89]: cols = ['Category','Rating']
In [90]: x = explode(df, cols)
In [91]: x
Out[91]:
User Category Rating
0 1 1 5
1 1 2 1
2 1 3 3
3 2 3 3
4 2 2 1
5 2 1 1
6 3 1 2
7 3 3 1
8 3 1 4
Now we can easily do what you need:
In [92]: x.loc[x.User == 1, cols].to_dict('r')
Out[92]:
[{'Category': '1', 'Rating': '5'},
{'Category': '2', 'Rating': '1'},
{'Category': '3', 'Rating': '3'}]