An answer similar to Mario's but with a single pass through the data (and keeps the "project" column as the last one):
data = [{"id": 1, "name": "Alex", "projects": ["A", "B", "C"]},
{"id": 2, "name": "Bob", "projects": ["D", "E"]},
{"id": 3, "name": "Charlie", "projects": None},
{"id": 4, "name": "Devin", "projects": []},
{"id": 5, "name": "Ellen", "projects": ["F", "G"]},]
# create a new excel file
import openpyxl
wb = openpyxl.Workbook()
# fill the first row with the keys of the first dictionary
for key in data[0].keys():
wb.active.cell(row=1, column=list(data[0].keys()).index(key) + 1).value = key
current_row = 2
for item in data:
item_without_projects = item.copy()
item_without_projects.pop("projects")
# handle the case where projects is None or an empty list
if item["projects"] is None or len(item["projects"]) == 0:
for col, key in enumerate(item_without_projects.keys()):
wb.active.cell(row=current_row, column=col + 1).value = str(item[key])
current_row += 1
continue
# fill a row with data for each project
for project in item["projects"]:
max_col = 0
for col, key in enumerate(item_without_projects.keys()):
wb.active.cell(row=current_row, column=col + 1).value = str(item[key])
max_col = max(max_col, col + 1)
wb.active.cell(row=current_row, column=max_col + 1).value = project
current_row += 1
# merge the newly created cells
for col, key in enumerate(item_without_projects.keys()):
print(current_row - len(item["projects"]), current_row, col + 1)
wb.active.merge_cells(start_row=current_row - len(item["projects"]), start_column=col + 1, end_row=current_row - 1, end_column=col + 1)
# style: set text alignment to center
cell = wb.active.cell(row=current_row - len(item["projects"]), column=col + 1)
cell.alignment = openpyxl.styles.Alignment(horizontal='center', vertical='center')
# write the workbook
wb.save("data.xlsx")
This clearly doesn't use pandas, but from the other comments you posted I understand .explode won't solve your problem. I'd be happy to answer if you can better formulate the various issues for the data loading
Reshaping and pivot tablespage. Once you load the data into a dataframe you probably only needdf.explode('projects')