Skip to content

Commit cd86492

Browse files
authored
Merge branch 'master' into jo
2 parents 7ae8e71 + b848295 commit cd86492

28 files changed

Lines changed: 181353 additions & 179 deletions

.github/workflows/deploy.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ name: Deploy Hugo site to Pages
44
on:
55
# Runs on pushes targeting the default branch
66
push:
7-
branches: ["website"]
7+
branches: ["master"]
88

99
# Allows you to run this workflow manually from the Actions tab
1010
workflow_dispatch:

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ Using the second dataset to enrich our analysis, we could explore
4646
4747
Please see the [Exploratory Data Analysis Report for the International Bestsellers](eda_report.md) and the notebook for the [Best book ever dataset](EDA_Best_Books_Ever.ipynb) for our results.
4848

49+
Please see the [Exploratory Data Analysis Report](eda_report.md) for detailed statistics, insights, and visualizations of the dataset.
50+
4951
### Related work
5052

5153
> - What others have already done with the data?

data/data_prep.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,9 @@ def guess_gender(authors: str):
6666
df_known = df[df['gender'].isin(["m", "w", "m;m", "w;m", "m;w", "w;w"])]
6767
df_known = df_known.drop_duplicates(subset=["title"])
6868

69-
merged = pd.merge(best_books, international_bestsellers[['title', 'gender']], on='title', how='inner')
70-
merged = merged[["title","author", "rating", "genres", "language", "gender"]]
69+
merged = pd.merge(best_books, international_bestsellers[['title', 'gender', 'nationality']], on='title', how='inner')
70+
merged = merged[["title","author", "nationality", "rating", "genres", "language", "gender"]]
71+
print(merged.columns)
7172
df_merged = pd.concat([df_known, merged])
7273
df_merged = df_merged.drop_duplicates(subset=["title"])
7374
df_merged.to_csv("datasets/genders_ratings.csv", index=False)

data/generate_bookshelf_csv.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import pandas as pd
2+
import os
3+
4+
"""
5+
Generate bookshelf.csv for the visualization
6+
Columns: title, author, rating, genres, language, gender, description
7+
8+
Uses existing gender data from genders_ratings.csv and adds description from best_books
9+
"""
10+
11+
cwd = os.getcwd().split("/")[-1]
12+
if cwd != "data":
13+
raise ValueError('Please run this script from inside the data directory')
14+
15+
# Load datasets
16+
best_books = pd.read_csv("datasets/books_1.Best_Books_Ever.csv")
17+
genders_ratings = pd.read_csv("datasets/genders_ratings.csv")
18+
19+
# Keep only needed columns from best_books
20+
best_books_clean = best_books[["title", "description", "publishDate"]].copy()
21+
22+
# Merge with gender data
23+
df = pd.merge(genders_ratings[["title", "author", "rating", "genres", "language", "gender"]],
24+
best_books_clean,
25+
on="title",
26+
how="left")
27+
28+
# Select required columns in order
29+
df = df[["title", "author", "rating", "genres", "language", "gender", "description", "publishDate"]]
30+
31+
# Remove rows without description
32+
df = df.dropna(subset=["description"])
33+
34+
# Remove duplicates
35+
df = df.drop_duplicates(subset=["title"])
36+
37+
# Save to website static folder
38+
output_path = "../website/static/data/bookshelf.csv"
39+
df.to_csv(output_path, index=False)
40+
41+
print(f"Generated {len(df)} books")
42+
print(f"Saved to {output_path}")
43+
print(f"Female authors: {len(df[df['gender'] == 'w'])}")
44+
print(f"Male authors: {len(df[df['gender'] == 'm'])}")

data/generate_publisher_csv.py

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
import pandas as pd
2+
import os
3+
import re
4+
import ast
5+
from collections import Counter
6+
7+
"""
8+
Generate a publisher-level CSV with counts of male/female authors per publisher.
9+
10+
Output columns: publisher, num_male, num_female
11+
12+
Notes:
13+
- Counts are computed by counting occurrences of 'm' and 'w' in the `gender` field
14+
(e.g. 'm;m' counts as 2 males). Missing publishers are set to 'Unknown'.
15+
"""
16+
17+
cwd = os.getcwd().split("/")[-1]
18+
if cwd != "data":
19+
raise ValueError('Please run this script from inside the data directory')
20+
21+
# Load datasets
22+
best_books = pd.read_csv("datasets/books_1.Best_Books_Ever.csv")
23+
genders_ratings = pd.read_csv("datasets/genders_ratings.csv")
24+
25+
# Keep only needed columns from best_books
26+
best_books_clean = best_books[["title", "publisher", "publishDate"]].copy()
27+
28+
# Merge with gender data
29+
df = pd.merge(
30+
genders_ratings[["title", "author", "rating", "genres", "language", "gender"]],
31+
best_books_clean,
32+
on="title",
33+
how="left",
34+
)
35+
36+
# Select required columns in order and drop duplicate titles
37+
df = df[["title", "author", "rating", "gender", "genres", "publisher", "publishDate"]]
38+
df = df.drop_duplicates(subset=["title"])
39+
40+
# Normalize publisher values and gender field
41+
df = df.dropna(subset=["publisher"])
42+
df["gender"] = df["gender"].fillna("").astype(str)
43+
44+
# Parse genres column
45+
def parse_genres(s):
46+
if pd.isna(s) or s == "":
47+
return []
48+
try:
49+
res = ast.literal_eval(s)
50+
if "Fiction" in res:
51+
res.remove("Fiction")
52+
return res
53+
except:
54+
return []
55+
56+
df["genres_list"] = df["genres"].apply(parse_genres)
57+
58+
# Count male/female occurrences in the gender string (simple, robust for 'm', 'w', 'm;m', 'w;w')
59+
def count_genders(s: str):
60+
s_low = s.lower()
61+
# count occurrences of single-letter tokens 'm' and 'w'
62+
num_m = s_low.count('m')
63+
num_w = s_low.count('w')
64+
return num_m, num_w
65+
66+
counts = df["gender"].apply(lambda s: pd.Series(dict(zip(["male", "female"], count_genders(s)))))
67+
df["male_count"] = counts["male"]
68+
df["female_count"] = counts["female"]
69+
70+
# Aggregate per publisher
71+
def get_top_genres(group_genres_list):
72+
"""Extract top 3 genres from a group of genre lists"""
73+
all_genres = []
74+
for genres_list in group_genres_list:
75+
all_genres.extend(genres_list)
76+
77+
if not all_genres:
78+
return ""
79+
80+
counter = Counter(all_genres)
81+
top_3 = counter.most_common(3)
82+
return ", ".join([genre for genre, count in top_3])
83+
84+
agg = (
85+
df.groupby("publisher", dropna=False)
86+
.agg(
87+
num_male=("male_count", "sum"),
88+
num_female=("female_count", "sum"),
89+
top_genres=("genres_list", get_top_genres)
90+
)
91+
.reset_index()
92+
)
93+
94+
# Optional: sort by total authors (descending)
95+
agg["total"] = agg["num_male"] + agg["num_female"]
96+
agg = agg.sort_values("total", ascending=False).drop(columns=["total"])
97+
98+
# Save aggregated output
99+
output_path = "../website/static/data/publisher_gender_counts.csv"
100+
agg.to_csv(output_path, index=False)
101+
102+
print(f"Generated {len(agg)} publishers")
103+
print(f"Saved publisher gender counts to {output_path}")

0 commit comments

Comments
 (0)