2

I have a dictionary as follows:

{'header_1': ['body_1', 'body_3', 'body_2'],
 'header_2': ['body_6', 'body_4', 'body_5'],
 'header_4': ['body_7', 'body_8'],
 'header_3': ['body_9'],
 'header_9': ['body_10'],
 'header_10': []}

I would like to come up with a dataframe like this:

+----+----------+--------+
| ID | header   | body   |
+----+----------+--------+
| 1  | header_1 | body_1 |
+----+----------+--------+
| 2  | header_1 | body_3 |
+----+----------+--------+
| 3  | header_1 | body_2 |
+----+----------+--------+
| 4  | header_2 | body_6 |
+----+----------+--------+
| 5  | header_2 | body_4 |
+----+----------+--------+
| 6  | header_2 | body_5 |
+----+----------+--------+
| 7  | header_4 | body_7 |
+----+----------+--------+

Where blank items (such as for the key header_10 in the dict above) would receive a value of None. I have tried a number of varieties for df.loc such as:

for header_name, body_list in all_unique.items():
    for body_name in body_list:
        metadata.loc[metadata.index[-1]] = [header_name, body_name]

To no avail. Surely there must be a quick way in Pandas to append rows and autoincrement the index? Something similar to the SQL INSERT INTO statement only using pythonic code?

halfer
  • 19,824
  • 17
  • 99
  • 186
user32882
  • 5,094
  • 5
  • 43
  • 82

3 Answers3

6

Use dict comprehension for add Nones for empty lists and then flatten for list of tuples:

d = {'header_1': ['body_1', 'body_3', 'body_2'],
 'header_2': ['body_6', 'body_4', 'body_5'],
 'header_4': ['body_7', 'body_8'],
 'header_3': ['body_9'],
 'header_9': ['body_10'],
 'header_10': []}

d = {k: v if bool(v) else [None] for k, v in d.items()}
data = [(k, y) for k, v in d.items() for y in v]
df = pd.DataFrame(data, columns= ['a','b'])
print (df)
            a        b
0    header_1   body_1
1    header_1   body_3
2    header_1   body_2
3    header_2   body_6
4    header_2   body_4
5    header_2   body_5
6    header_4   body_7
7    header_4   body_8
8    header_3   body_9
9    header_9  body_10
10  header_10     None

Another solution:

data = []
for k, v in d.items():
    if bool(v):
        for y in v:
            data.append((k, y))
    else:
        data.append((k, None))


df = pd.DataFrame(data, columns= ['a','b'])
print (df)
            a        b
0    header_1   body_1
1    header_1   body_3
2    header_1   body_2
3    header_2   body_6
4    header_2   body_4
5    header_2   body_5
6    header_4   body_7
7    header_4   body_8
8    header_3   body_9
9    header_9  body_10
10  header_10     None
jezrael
  • 822,522
  • 95
  • 1,334
  • 1,252
2

This is another unnesting problem again

Borrow Jez's setting up for your d

d = {k: v if bool(v) else [None] for k, v in d.items()}

1st convert your dict into dataframe

df=pd.Series(d).reset_index()
df.columns
Out[204]: Index(['index', 0], dtype='object')

Then using this function in here

yourdf=unnesting(df,[0])
yourdf
Out[208]: 
         0      index
0   body_1   header_1
0   body_3   header_1
0   body_2   header_1
1   body_6   header_2
1   body_4   header_2
1   body_5   header_2
2   body_7   header_4
2   body_8   header_4
3   body_9   header_3
4  body_10   header_9
5     None  header_10

def unnesting(df, explode):
    idx=df.index.repeat(df[explode[0]].str.len())
    df1=pd.concat([pd.DataFrame({x:np.concatenate(df[x].values)} )for x in explode],axis=1)
    df1.index=idx
    return df1.join(df.drop(explode,1),how='left')
BENY
  • 317,841
  • 20
  • 164
  • 234
2

If the dataset is too big, this solution would be slow, but it should still work.

for key in data.keys():
    vals= data[key]
    # Create temp df with data from a single key
    t_df = pd.DataFrame({'header':[key]*len(vals),'body':vals})

    # Append it to your full dataframe.
    df = df.append(t_df)
Polkaguy6000
  • 1,150
  • 1
  • 8
  • 15