hmb HF Staff commited on
Commit
74ead6a
ยท
verified ยท
1 Parent(s): 319a82d

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +220 -0
app.py ADDED
@@ -0,0 +1,220 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import pandas as pd
3
+
4
+ from nomad_data import country_emoji_map, data
5
+
6
+ # Create dataframe from imported data
7
+ df = pd.DataFrame(data)
8
+
9
+ # Create styling functions
10
+ def style_quality_of_life(val):
11
+ """Style the Quality of Life column with color gradient from red to green"""
12
+ if pd.isna(val):
13
+ # Special styling for null/missing values
14
+ return 'background-color: rgba(200, 200, 200, 0.2); color: #999; font-style: italic;'
15
+
16
+ # Define min and max values for Quality of Life (typically on a scale of 0-10)
17
+ min_val = 5.0 # Anything below this will be bright red
18
+ max_val = 9.0 # Anything above this will be bright green
19
+
20
+ # Normalize value between 0 and 1
21
+ normalized = (val - min_val) / (max_val - min_val)
22
+ # Clamp between 0 and 1
23
+ normalized = max(0, min(normalized, 1))
24
+
25
+ # Calculate percentage fill for gradient
26
+ percentage = int(normalized * 100)
27
+
28
+ # Create a linear gradient based on the normalized value
29
+ if normalized < 0.5:
30
+ # Red to yellow gradient
31
+ start_color = f"rgba(255, {int(255 * (normalized * 2))}, 0, 0.3)"
32
+ end_color = "rgba(255, 255, 255, 0)"
33
+ else:
34
+ # Yellow to green gradient
35
+ start_color = f"rgba({int(255 * (1 - (normalized - 0.5) * 2))}, 255, 0, 0.3)"
36
+ end_color = "rgba(255, 255, 255, 0)"
37
+
38
+ return f'background: linear-gradient(to right, {start_color} {percentage}%, {end_color} {percentage}%)'
39
+
40
+ def style_internet_speed(val):
41
+ """Style the Internet Speed column from red (slow) to green (fast)"""
42
+ if pd.isna(val):
43
+ # Special styling for null/missing values
44
+ return 'background-color: rgba(200, 200, 200, 0.2); color: #999; font-style: italic;'
45
+
46
+ # Define min and max values
47
+ min_val = 20 # Slow internet
48
+ max_val = 300 # Fast internet
49
+
50
+ # Normalize value between 0 and 1
51
+ normalized = (val - min_val) / (max_val - min_val)
52
+ # Clamp between 0 and 1
53
+ normalized = max(0, min(normalized, 1))
54
+
55
+ # Calculate percentage fill for gradient
56
+ percentage = int(normalized * 100)
57
+
58
+ # Create a linear gradient based on the normalized value
59
+ if normalized < 0.5:
60
+ # Red to yellow gradient
61
+ start_color = f"rgba(255, {int(255 * (normalized * 2))}, 0, 0.3)"
62
+ end_color = "rgba(255, 255, 255, 0)"
63
+ else:
64
+ # Yellow to green gradient
65
+ start_color = f"rgba({int(255 * (1 - (normalized - 0.5) * 2))}, 255, 0, 0.3)"
66
+ end_color = "rgba(255, 255, 255, 0)"
67
+
68
+ return f'background: linear-gradient(to right, {start_color} {percentage}%, {end_color} {percentage}%)'
69
+
70
+ def style_dataframe(df):
71
+ """Apply styling to the entire dataframe"""
72
+ # Create a copy to avoid SettingWithCopyWarning
73
+ styled_df = df.copy()
74
+
75
+ # Convert to Styler object
76
+ styler = styled_df.style
77
+
78
+ # Apply styles to specific columns
79
+ styler = styler.applymap(style_quality_of_life, subset=['Quality of Life'])
80
+ styler = styler.applymap(style_internet_speed, subset=['Internet Speed (Mbps)'])
81
+
82
+ # Highlight null values in all columns
83
+ styler = styler.highlight_null(props='color: #999; font-style: italic; background-color: rgba(200, 200, 200, 0.2)')
84
+
85
+ # Format numeric columns
86
+ styler = styler.format({
87
+ 'Quality of Life': lambda x: f'{x:.1f}' if pd.notna(x) else 'Data Not Available',
88
+ 'Internet Speed (Mbps)': lambda x: f'{x:.1f}' if pd.notna(x) else 'Data Not Available',
89
+ 'Monthly Cost Living (USD)': lambda x: f'${x:.0f}' if pd.notna(x) else 'Data Not Available',
90
+ 'Visa Length (Months)': lambda x: f'{x:.0f}' if pd.notna(x) else 'Data Not Available',
91
+ 'Visa Cost (USD)': lambda x: f'${x:.0f}' if pd.notna(x) else 'Data Not Available',
92
+ 'Growth Trend (5 Years)': lambda x: f'{x}' if pd.notna(x) else 'Data Not Available'
93
+ })
94
+
95
+ return styler
96
+
97
+ def filter_data(country, max_cost):
98
+ """Filter data based on country and maximum cost of living"""
99
+ filtered_df = df.copy()
100
+
101
+ if country and country != "All":
102
+ filtered_df = filtered_df[filtered_df["Country"] == country]
103
+
104
+ # Filter by maximum cost of living (and handle null values)
105
+ if max_cost < df["Monthly Cost Living (USD)"].max():
106
+ # Include rows where cost is less than max_cost OR cost is null
107
+ cost_mask = (filtered_df["Monthly Cost Living (USD)"] <= max_cost) | (filtered_df["Monthly Cost Living (USD)"].isna())
108
+ filtered_df = filtered_df[cost_mask]
109
+
110
+ return style_dataframe(filtered_df)
111
+
112
+ # Function to get unique values for dropdowns with "All" option
113
+ def get_unique_values(column):
114
+ unique_values = ["All"] + sorted(df[column].unique().tolist())
115
+ return unique_values
116
+
117
+ # Add country emojis for the dropdown
118
+ def get_country_with_emoji(column):
119
+ choices_with_emoji = ["โœˆ๏ธ All"]
120
+ for c in df[column].unique():
121
+ if c in country_emoji_map:
122
+ choices_with_emoji.append(country_emoji_map[c])
123
+ else:
124
+ choices_with_emoji.append(c)
125
+ return sorted(choices_with_emoji)
126
+
127
+ # Initial styled dataframe
128
+ styled_df = style_dataframe(df)
129
+
130
+ with gr.Blocks(css="""
131
+ .gradio-container .table-wrap {
132
+ font-family: 'Inter', sans-serif;
133
+ }
134
+ .gradio-container table td, .gradio-container table th {
135
+ text-align: left;
136
+ }
137
+ .gradio-container table th {
138
+ background-color: #f3f4f6;
139
+ font-weight: 600;
140
+ }
141
+ /* Style for null values */
142
+ .null-value {
143
+ color: #999;
144
+ font-style: italic;
145
+ background-color: rgba(200, 200, 200, 0.2);
146
+ }
147
+ """) as demo:
148
+ gr.Markdown("# ๐ŸŒ Digital Nomad Destinations")
149
+ gr.Markdown("Explore top digital nomad locations around the world. The bars in numeric columns indicate relative values - longer bars are better!")
150
+
151
+ with gr.Row():
152
+ country_dropdown = gr.Dropdown(
153
+ choices=get_country_with_emoji("Country"),
154
+ value="โœˆ๏ธ All",
155
+ label="๐ŸŒ Filter by Country"
156
+ )
157
+
158
+ cost_slider = gr.Slider(
159
+ minimum=500,
160
+ maximum=4000,
161
+ value=4000,
162
+ step=100,
163
+ label="๐Ÿ’ฐ Maximum Monthly Cost of Living (USD)"
164
+ )
165
+
166
+
167
+ data_table = gr.Dataframe(
168
+ value=styled_df,
169
+ datatype=["str", "str", "number", "number", "number", "str", "number", "number", "str", "str"],
170
+ max_height=600,
171
+ interactive=False,
172
+ show_copy_button=True,
173
+ show_row_numbers=True,
174
+ show_search=True,
175
+ show_fullscreen_button=True,
176
+ pinned_columns=2
177
+ )
178
+
179
+ # Update data when filters change
180
+ def process_country_filter(country, cost):
181
+ # Remove emoji from country name if present
182
+ if country and country.startswith("โœˆ๏ธ All"):
183
+ country = "All"
184
+ else:
185
+ for emoji_code in ["๐Ÿ‡ง๐Ÿ‡ท", "๐Ÿ‡ญ๐Ÿ‡บ", "๐Ÿ‡บ๐Ÿ‡พ", "๐Ÿ‡ต๐Ÿ‡น", "๐Ÿ‡ฌ๐Ÿ‡ช", "๐Ÿ‡น๐Ÿ‡ญ", "๐Ÿ‡ฆ๐Ÿ‡ช", "๐Ÿ‡ช๐Ÿ‡ธ", "๐Ÿ‡ฎ๐Ÿ‡น", "๐Ÿ‡จ๐Ÿ‡ฆ", "๐Ÿ‡จ๐Ÿ‡ด", "๐Ÿ‡ฒ๐Ÿ‡ฝ", "๐Ÿ‡ฏ๐Ÿ‡ต", "๐Ÿ‡ฐ๐Ÿ‡ท"]:
186
+ if country and emoji_code in country:
187
+ country = country.split(" ", 1)[1]
188
+ break
189
+
190
+ filtered_df = df.copy()
191
+
192
+ # Filter by country
193
+ if country and country != "All":
194
+ filtered_df = filtered_df[filtered_df["Country"] == country]
195
+
196
+ # Filter by cost with special handling for nulls
197
+ if cost < df["Monthly Cost Living (USD)"].max():
198
+ cost_mask = (filtered_df["Monthly Cost Living (USD)"] <= cost) & (filtered_df["Monthly Cost Living (USD)"].notna())
199
+
200
+ filtered_df = filtered_df[cost_mask]
201
+
202
+ return style_dataframe(filtered_df)
203
+
204
+ country_dropdown.change(process_country_filter, [country_dropdown, cost_slider], data_table)
205
+ cost_slider.change(process_country_filter, [country_dropdown, cost_slider], data_table)
206
+
207
+ gr.Markdown("### ๐Ÿ“Š Data Visualization Guide")
208
+ gr.Markdown("The table above uses colorful gradient bars to help you quickly identify: \n"
209
+ "- **๐ŸŒŸ Quality of Life**: Longer green bars indicate higher quality of life \n"
210
+ "- **๐Ÿš€ Internet Speed**: Longer green bars indicate faster internet connections \n"
211
+ "- **๐Ÿ’ต Cost of Living**: Values shown as dollar amounts without color coding \n"
212
+ "- **โ“ Missing Data**: Displayed as *Data Not Available* with a light gray background")
213
+
214
+ gr.Markdown("### ๐Ÿงณ Digital Nomad Tips")
215
+ gr.Markdown("- Look for places with digital nomad visas for longer stays \n"
216
+ "- Consider internet speed if you need to attend video meetings \n"
217
+ "- Balance cost of living with quality of life for the best experience \n"
218
+ "- Some newer nomad destinations may have incomplete data")
219
+
220
+ demo.launch()