mirix commited on
Commit
a71ca97
·
verified ·
1 Parent(s): 1273f3b

Upload 3 files

Browse files
Hurun_Global_Rich_List_2025_COORD.csv ADDED
The diff for this file is too large to render. See raw diff
 
app.py ADDED
@@ -0,0 +1,201 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import pandas as pd
3
+ import plotly.express as px
4
+ import plotly.graph_objects as go
5
+ import numpy as np
6
+ import math
7
+ import random
8
+
9
+ # Load and preprocess data
10
+ df = pd.read_csv('Hurun_Global_Rich_List_2025_COORD.csv')
11
+ df['Wealth $B'] = pd.to_numeric(df['Wealth $B'], errors='coerce')
12
+ df = df.dropna(subset=['lat', 'lon'])
13
+ df['Log Wealth'] = np.log10(df['Wealth $B'])
14
+
15
+ # Extract country from location
16
+ df['Country'] = df['Location'].str.split(',').str[-1].str.strip()
17
+
18
+ # Add jitter to coordinates to prevent overlapping
19
+ def add_jitter(coords, jitter_range=0.2):
20
+ """Add random jitter to coordinates to prevent overlapping markers"""
21
+ # Create a dictionary to track coordinate counts
22
+ coord_counts = {}
23
+ jittered_coords = []
24
+
25
+ for coord in coords:
26
+ # Round coordinates to 2 decimal places for grouping
27
+ rounded = (round(coord[0], 2), round(coord[1], 2))
28
+
29
+ # Count how many times we've seen this approximate location
30
+ count = coord_counts.get(rounded, 0)
31
+ coord_counts[rounded] = count + 1
32
+
33
+ # Only add jitter if there are multiple points at this location
34
+ if count > 0:
35
+ # Calculate jitter based on count to spread points
36
+ angle = 2 * math.pi * (count / 8) # Spread in 8 directions
37
+ distance = jitter_range * min(0.5 + count/10, 1.5) # Scale with count
38
+
39
+ jitter_lat = math.sin(angle) * distance
40
+ jitter_lon = math.cos(angle) * distance
41
+
42
+ jittered_coords.append((coord[0] + jitter_lat, coord[1] + jitter_lon))
43
+ else:
44
+ jittered_coords.append(coord)
45
+
46
+ return jittered_coords
47
+
48
+ # Create logarithmic scale for visualization
49
+ def create_globe():
50
+ # Calculate logarithmic values for visualization
51
+ df['Size'] = df['Log Wealth'] * 8
52
+ df['Color Value'] = df['Log Wealth']
53
+
54
+ # Add jitter to coordinates
55
+ coords = list(zip(df['lat'], df['lon']))
56
+ jittered_coords = add_jitter(coords)
57
+ df['lat_jitter'] = [c[0] for c in jittered_coords]
58
+ df['lon_jitter'] = [c[1] for c in jittered_coords]
59
+
60
+ # Generate linear tick positions for legend
61
+ min_log = df['Log Wealth'].min()
62
+ max_log = df['Log Wealth'].max()
63
+
64
+ wealth_ticks = [1, 10, 30, 100, 300, 1000]
65
+ log_ticks = [math.log10(x) for x in wealth_ticks if x <= 10**max_log and x >= 10**min_log]
66
+ tick_labels = [f"${x}B" for x in wealth_ticks if x <= 10**max_log and x >= 10**min_log]
67
+
68
+ fig = go.Figure(go.Scattergeo(
69
+ lon = df['lon_jitter'],
70
+ lat = df['lat_jitter'],
71
+ text = df.apply(lambda row: (
72
+ f"<b>{row['Name']}</b><br>"
73
+ f"Rank: {row['Rank']}<br>"
74
+ f"Wealth: ${row['Wealth $B']}B<br>"
75
+ f"Enterprise: {row['Enterprise']}<br>"
76
+ f"Sector: {row['Sector']}<br>"
77
+ f"Age: {row['Age']}<br>"
78
+ f"Location: {row['Location']}"
79
+ ), axis=1),
80
+ marker = dict(
81
+ size = df['Size'],
82
+ color = df['Color Value'],
83
+ colorscale = 'Turbo',
84
+ opacity = 0.7,
85
+ line_color='rgb(40,40,40)',
86
+ line_width=0.5,
87
+ sizemode = 'diameter',
88
+ colorbar = dict(
89
+ title="Wealth (Billion USD)",
90
+ thickness=15,
91
+ len=0.35,
92
+ bgcolor='rgba(255,255,255,0.5)',
93
+ tickvals = log_ticks,
94
+ ticktext = tick_labels
95
+ )
96
+ ),
97
+ hoverinfo = 'text',
98
+ name = ''
99
+ ))
100
+
101
+ fig.update_geos(
102
+ projection_type="orthographic",
103
+ showcoastlines=True,
104
+ coastlinecolor="RebeccaPurple",
105
+ showland=True,
106
+ landcolor="rgb(243, 243, 243)",
107
+ showocean=True,
108
+ oceancolor="rgb(217, 244, 252)"
109
+ )
110
+
111
+ fig.update_layout(
112
+ height=500,
113
+ margin={"r":0,"t":0,"l":0,"b":0},
114
+ paper_bgcolor='rgba(0,0,0,0)',
115
+ geo=dict(bgcolor='rgba(0,0,0,0)')
116
+ )
117
+ return fig
118
+
119
+ # Create distribution plots
120
+ def create_age_plot():
121
+ fig = px.histogram(df, x='Age', nbins=20,
122
+ title='Age Distribution',
123
+ color_discrete_sequence=['#636EFA'])
124
+ fig.update_layout(height=300)
125
+ return fig
126
+
127
+ def create_country_plot():
128
+ top_countries = df['Country'].value_counts().head(10)
129
+ fig = px.bar(top_countries,
130
+ title='Top 10 Countries',
131
+ labels={'value': 'Count', 'index': 'Country'},
132
+ color=top_countries.index,
133
+ color_discrete_sequence=px.colors.qualitative.Pastel)
134
+ fig.update_layout(height=300, showlegend=False)
135
+ return fig
136
+
137
+ def create_gender_plot():
138
+ gender_counts = df['Sex'].value_counts()
139
+ fig = px.pie(gender_counts,
140
+ names=gender_counts.index,
141
+ values=gender_counts.values,
142
+ title='Gender Distribution',
143
+ hole=0.4)
144
+ fig.update_layout(height=300)
145
+ return fig
146
+
147
+ def create_sector_plot():
148
+ top_sectors = df['Sector'].value_counts().head(10)
149
+ fig = px.bar(top_sectors,
150
+ title='Top 10 Sectors',
151
+ labels={'value': 'Count', 'index': 'Sector'},
152
+ color=top_sectors.index,
153
+ color_discrete_sequence=px.colors.qualitative.Pastel)
154
+ fig.update_layout(height=300, showlegend=False)
155
+ return fig
156
+
157
+ # Create display dataframe without extra columns
158
+ display_columns = ['Rank', 'Rank change', 'Wealth $B', 'Enterprise', 'Sector',
159
+ 'Name', 'Sex', 'Age', 'Sidekick', 'Location']
160
+ display_df = df[display_columns].sort_values('Rank').reset_index(drop=True)
161
+
162
+ # Create Gradio interface
163
+ with gr.Blocks(theme=gr.themes.Soft()) as demo:
164
+ gr.Markdown("# 🗺️ Global Billionaires Dashboard 2025")
165
+ gr.Markdown("Interactive visualisation of the world's wealthiest individuals")
166
+
167
+ with gr.Row():
168
+ with gr.Column():
169
+ gr.Markdown("### 🌍 Global Wealth Distribution")
170
+ gr.Markdown("Circle size and color intensity both represent wealth on a logarithmic scale")
171
+ gr.Markdown("Nearby points are slightly randomized to prevent overlapping")
172
+ globe = gr.Plot(create_globe(), label="Globe")
173
+
174
+ with gr.Row():
175
+ with gr.Column():
176
+ gr.Markdown("### 📊 Distribution Analysis")
177
+
178
+ with gr.Row():
179
+ with gr.Column():
180
+ age_plot = gr.Plot(create_age_plot())
181
+ with gr.Column():
182
+ country_plot = gr.Plot(create_country_plot())
183
+
184
+ with gr.Row():
185
+ with gr.Column():
186
+ gender_plot = gr.Plot(create_gender_plot())
187
+ with gr.Column():
188
+ sector_plot = gr.Plot(create_sector_plot())
189
+
190
+ with gr.Row():
191
+ with gr.Column():
192
+ gr.Markdown("### 📋 Full Billionaire Data")
193
+ data_table = gr.Dataframe(
194
+ value=display_df,
195
+ interactive=True,
196
+ column_widths=["auto"] * len(display_columns),
197
+ wrap=True,
198
+ col_count=(len(display_columns), "fixed")
199
+ )
200
+
201
+ demo.launch()
requirements.txt ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ plotly
2
+ gradio
3
+ pandas
4
+ numpy
5
+ lxml