Vishwas1 commited on
Commit
5542a6a
·
verified ·
1 Parent(s): feeee67

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +75 -47
app.py CHANGED
@@ -1,4 +1,3 @@
1
-
2
  import math
3
  from typing import Dict, List, Optional
4
 
@@ -8,6 +7,9 @@ import numpy as np
8
  import matplotlib.pyplot as plt
9
  from periodictable import elements
10
 
 
 
 
11
  NUMERIC_PROPS = [
12
  ("mass", "Atomic mass (u)"),
13
  ("density", "Density (g/cm^3)"),
@@ -109,23 +111,33 @@ def build_elements_df() -> pd.DataFrame:
109
  return df
110
 
111
  DF = build_elements_df()
 
 
 
 
112
  MAX_GROUP = 18
113
  MAX_PERIOD = 7
114
-
115
  GRID: List[List[Optional[int]]] = [[None for _ in range(MAX_GROUP)] for _ in range(MAX_PERIOD)]
 
116
  for _, row in DF.iterrows():
117
- period, group, Z = int(row["period"]), row["group"], int(row["Z"])
118
- if group is None:
 
 
 
119
  continue
120
- GRID[period-1][group-1] = Z
 
121
 
122
- LAN = [z for z in DF["Z"] if 57 <= z <= 71]
123
- ACT = [z for z in DF["Z"] if 89 <= z <= 103]
124
 
 
 
 
125
  def plot_trend(trend_df: pd.DataFrame, prop_key: str, Z: int, symbol: str):
126
  fig, ax = plt.subplots()
127
  ax.scatter(trend_df["Z"], trend_df[prop_key])
128
- # highlight
129
  sel = trend_df.loc[trend_df["Z"] == Z, prop_key]
130
  if not sel.empty and not pd.isna(sel.values[0]):
131
  ax.scatter([Z], [sel.values[0]], s=80)
@@ -144,16 +156,15 @@ def plot_heatmap(property_key: str):
144
  z = GRID[r][c]
145
  if z is None:
146
  continue
147
- val = DF.loc[DF['Z'] == z, property_key].values[0]
148
  if not pd.isna(val):
149
  grid_vals[r, c] = float(val)
150
-
151
  fig, ax = plt.subplots()
152
  im = ax.imshow(grid_vals, origin="upper", aspect="auto")
153
  ax.set_xticks(range(MAX_GROUP))
154
- ax.set_xticklabels([str(i) for i in range(1, MAX_GROUP+1)])
155
  ax.set_yticks(range(MAX_PERIOD))
156
- ax.set_yticklabels([str(i) for i in range(1, MAX_PERIOD+1)])
157
  ax.set_xlabel("Group")
158
  ax.set_ylabel("Period")
159
  ax.set_title(f"Periodic heatmap: {prop_label}")
@@ -161,6 +172,9 @@ def plot_heatmap(property_key: str):
161
  fig.tight_layout()
162
  return fig
163
 
 
 
 
164
  def element_info(z_or_symbol: str):
165
  try:
166
  if z_or_symbol.isdigit():
@@ -172,29 +186,33 @@ def element_info(z_or_symbol: str):
172
  except Exception:
173
  return f"Unknown element: {z_or_symbol}", None, None
174
 
175
- row = DF.loc[DF['Z'] == Z].iloc[0].to_dict()
176
- symbol = row['symbol']
177
 
178
  facts = []
179
  facts.extend(CURATED_FACTS.get(symbol, []))
180
- facts.append(GROUP_FACTS.get(row['category'], None))
181
  facts = [f for f in facts if f]
182
 
183
  props_lines = [
184
  f"{row['name']} ({symbol}), Z = {Z}",
185
- f"Period {int(row['period'])}, Group {row['group']}, Block {row['block']} | Category: {row['category'].replace('-', ' ').title()}",
 
 
186
  f"Atomic mass: {row['mass'] if row['mass'] else '—'} u",
187
  f"Density: {row['density'] if row['density'] else '—'} g/cm³",
188
  f"Electronegativity: {row['electronegativity'] if row['electronegativity'] else '—'} (Pauling)",
189
- f"Melting point: {row['melting_point'] if row['melting_point'] else '—'} K | Boiling point: {row['boiling_point'] if row['boiling_point'] else '—'} K",
190
- f"vdW radius: {row['vdw_radius'] if row['vdw_radius'] else '—'} pm | Covalent radius: {row['covalent_radius'] if row['covalent_radius'] else '—'} pm",
 
 
191
  f"Radioactive: {'Yes' if row['is_radioactive'] else 'No'}",
192
  ]
193
  info_text = "\n".join(props_lines)
194
  facts_text = "\n• ".join(["Interesting facts:"] + facts) if facts else "No fact on file—still cool though!"
195
 
196
- prop_key = 'electronegativity' if not pd.isna(row['electronegativity']) else 'mass'
197
- trend_df = DF[['Z', 'symbol', prop_key]].dropna()
198
  fig = plot_trend(trend_df, prop_key, Z, symbol)
199
 
200
  return info_text, facts_text, fig
@@ -203,20 +221,45 @@ def handle_button_click(z: int):
203
  return element_info(str(z))
204
 
205
  def search_element(query: str):
206
- query = (query or '').strip()
207
  if not query:
208
  return gr.update(), gr.update(), gr.update()
209
  return element_info(query)
210
 
 
 
 
211
  with gr.Blocks(title="Interactive Periodic Table") as demo:
212
  gr.Markdown("# 🧪 Interactive Periodic Table\nClick an element or search by symbol/name/atomic number.")
213
 
214
  with gr.Row():
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
215
  with gr.Column(scale=2):
216
  gr.Markdown("### Main Table")
 
217
  with gr.Row():
218
  for g in range(1, 19):
219
  gr.Markdown(f"**{g}**")
 
 
220
  for r in range(MAX_PERIOD):
221
  with gr.Row():
222
  for c in range(MAX_GROUP):
@@ -224,41 +267,26 @@ with gr.Blocks(title="Interactive Periodic Table") as demo:
224
  if z is None:
225
  gr.Button("", interactive=False)
226
  else:
227
- sym = DF.loc[DF['Z'] == z, 'symbol'].values[0]
228
  btn = gr.Button(sym)
229
- btn.click(handle_button_click, inputs=[gr.Number(z, visible=False)], outputs=[
230
- gr.Textbox(interactive=False), gr.Markdown(), gr.Matplotlib()])
231
 
232
  gr.Markdown("### f-block (lanthanides & actinides)")
233
  with gr.Row():
234
  for z in LAN:
235
- sym = DF.loc[DF['Z'] == z, 'symbol'].values[0]
236
  btn = gr.Button(sym)
237
- btn.click(handle_button_click, inputs=[gr.Number(z, visible=False)], outputs=[
238
- gr.Textbox(interactive=False), gr.Markdown(), gr.Matplotlib()])
239
  with gr.Row():
240
  for z in ACT:
241
- sym = DF.loc[DF['Z'] == z, 'symbol'].values[0]
242
  btn = gr.Button(sym)
243
- btn.click(handle_button_click, inputs=[gr.Number(z, visible=False)], outputs=[
244
- gr.Textbox(interactive=False), gr.Markdown(), gr.Matplotlib()])
245
-
246
- with gr.Column(scale=1):
247
- search = gr.Textbox(label="Search (symbol/name/Z)", placeholder="e.g., C, Iron, 79")
248
- info = gr.Textbox(label="Properties", lines=10, interactive=False)
249
- facts = gr.Markdown("Select an element to see fun facts.")
250
- trend = gr.Matplotlib()
251
-
252
- search.submit(search_element, inputs=[search], outputs=[info, facts, trend])
253
-
254
- gr.Markdown("### Trend heatmap")
255
- prop = gr.Dropdown(choices=[k for k, _ in NUMERIC_PROPS], value="electronegativity", label="Property")
256
- heat = gr.Matplotlib()
257
-
258
- def heatmap_callback(property_key):
259
- return plot_heatmap(property_key)
260
- prop.change(heatmap_callback, inputs=[prop], outputs=[heat])
261
- heat.update(plot_heatmap("electronegativity"))
262
 
263
  if __name__ == "__main__":
264
  demo.launch()
 
 
 
 
1
  import math
2
  from typing import Dict, List, Optional
3
 
 
7
  import matplotlib.pyplot as plt
8
  from periodictable import elements
9
 
10
+ # -----------------------------
11
+ # Data
12
+ # -----------------------------
13
  NUMERIC_PROPS = [
14
  ("mass", "Atomic mass (u)"),
15
  ("density", "Density (g/cm^3)"),
 
111
  return df
112
 
113
  DF = build_elements_df()
114
+
115
+ # -----------------------------
116
+ # Safe grid mapping (skip None period/group)
117
+ # -----------------------------
118
  MAX_GROUP = 18
119
  MAX_PERIOD = 7
 
120
  GRID: List[List[Optional[int]]] = [[None for _ in range(MAX_GROUP)] for _ in range(MAX_PERIOD)]
121
+
122
  for _, row in DF.iterrows():
123
+ period = row["period"]
124
+ group = row["group"]
125
+ z = int(row["Z"])
126
+ # Skip rows with missing period or group
127
+ if pd.isna(period) or group is None:
128
  continue
129
+ period = int(period)
130
+ GRID[period - 1][group - 1] = z
131
 
132
+ LAN = [int(z) for z in DF["Z"] if 57 <= int(z) <= 71]
133
+ ACT = [int(z) for z in DF["Z"] if 89 <= int(z) <= 103]
134
 
135
+ # -----------------------------
136
+ # Plotting helpers (Matplotlib)
137
+ # -----------------------------
138
  def plot_trend(trend_df: pd.DataFrame, prop_key: str, Z: int, symbol: str):
139
  fig, ax = plt.subplots()
140
  ax.scatter(trend_df["Z"], trend_df[prop_key])
 
141
  sel = trend_df.loc[trend_df["Z"] == Z, prop_key]
142
  if not sel.empty and not pd.isna(sel.values[0]):
143
  ax.scatter([Z], [sel.values[0]], s=80)
 
156
  z = GRID[r][c]
157
  if z is None:
158
  continue
159
+ val = DF.loc[DF["Z"] == z, property_key].values[0]
160
  if not pd.isna(val):
161
  grid_vals[r, c] = float(val)
 
162
  fig, ax = plt.subplots()
163
  im = ax.imshow(grid_vals, origin="upper", aspect="auto")
164
  ax.set_xticks(range(MAX_GROUP))
165
+ ax.set_xticklabels([str(i) for i in range(1, MAX_GROUP + 1)])
166
  ax.set_yticks(range(MAX_PERIOD))
167
+ ax.set_yticklabels([str(i) for i in range(1, MAX_PERIOD + 1)])
168
  ax.set_xlabel("Group")
169
  ax.set_ylabel("Period")
170
  ax.set_title(f"Periodic heatmap: {prop_label}")
 
172
  fig.tight_layout()
173
  return fig
174
 
175
+ # -----------------------------
176
+ # Callbacks
177
+ # -----------------------------
178
  def element_info(z_or_symbol: str):
179
  try:
180
  if z_or_symbol.isdigit():
 
186
  except Exception:
187
  return f"Unknown element: {z_or_symbol}", None, None
188
 
189
+ row = DF.loc[DF["Z"] == Z].iloc[0].to_dict()
190
+ symbol = row["symbol"]
191
 
192
  facts = []
193
  facts.extend(CURATED_FACTS.get(symbol, []))
194
+ facts.append(GROUP_FACTS.get(row["category"], None))
195
  facts = [f for f in facts if f]
196
 
197
  props_lines = [
198
  f"{row['name']} ({symbol}), Z = {Z}",
199
+ f"Period {int(row['period']) if not pd.isna(row['period']) else ''}, "
200
+ f"Group {row['group'] if row['group'] is not None else '—'}, "
201
+ f"Block {row['block']} | Category: {row['category'].replace('-', ' ').title()}",
202
  f"Atomic mass: {row['mass'] if row['mass'] else '—'} u",
203
  f"Density: {row['density'] if row['density'] else '—'} g/cm³",
204
  f"Electronegativity: {row['electronegativity'] if row['electronegativity'] else '—'} (Pauling)",
205
+ f"Melting point: {row['melting_point'] if row['melting_point'] else '—'} K | "
206
+ f"Boiling point: {row['boiling_point'] if row['boiling_point'] else '—'} K",
207
+ f"vdW radius: {row['vdw_radius'] if row['vdw_radius'] else '—'} pm | "
208
+ f"Covalent radius: {row['covalent_radius'] if row['covalent_radius'] else '—'} pm",
209
  f"Radioactive: {'Yes' if row['is_radioactive'] else 'No'}",
210
  ]
211
  info_text = "\n".join(props_lines)
212
  facts_text = "\n• ".join(["Interesting facts:"] + facts) if facts else "No fact on file—still cool though!"
213
 
214
+ prop_key = "electronegativity" if not pd.isna(row["electronegativity"]) else "mass"
215
+ trend_df = DF[["Z", "symbol", prop_key]].dropna()
216
  fig = plot_trend(trend_df, prop_key, Z, symbol)
217
 
218
  return info_text, facts_text, fig
 
221
  return element_info(str(z))
222
 
223
  def search_element(query: str):
224
+ query = (query or "").strip()
225
  if not query:
226
  return gr.update(), gr.update(), gr.update()
227
  return element_info(query)
228
 
229
+ # -----------------------------
230
+ # UI
231
+ # -----------------------------
232
  with gr.Blocks(title="Interactive Periodic Table") as demo:
233
  gr.Markdown("# 🧪 Interactive Periodic Table\nClick an element or search by symbol/name/atomic number.")
234
 
235
  with gr.Row():
236
+ # Create the inspector FIRST so button clicks can target these components
237
+ with gr.Column(scale=1):
238
+ gr.Markdown("### Inspector")
239
+ search = gr.Textbox(label="Search (symbol/name/Z)", placeholder="e.g., C, Iron, 79")
240
+ info = gr.Textbox(label="Properties", lines=10, interactive=False)
241
+ facts = gr.Markdown("Select an element to see fun facts.")
242
+ trend = gr.Matplotlib()
243
+
244
+ search.submit(search_element, inputs=[search], outputs=[info, facts, trend])
245
+
246
+ gr.Markdown("### Trend heatmap")
247
+ prop = gr.Dropdown(choices=[k for k, _ in NUMERIC_PROPS], value="electronegativity", label="Property")
248
+ heat = gr.Matplotlib()
249
+ prop.change(lambda k: plot_heatmap(k), inputs=[prop], outputs=[heat])
250
+
251
+ # Initialize heatmap on load
252
+ demo.load(lambda: plot_heatmap("electronegativity"), outputs=[heat])
253
+
254
+ # Now build the grid of buttons
255
  with gr.Column(scale=2):
256
  gr.Markdown("### Main Table")
257
+ # Headers (groups 1-18)
258
  with gr.Row():
259
  for g in range(1, 19):
260
  gr.Markdown(f"**{g}**")
261
+
262
+ # Element grid
263
  for r in range(MAX_PERIOD):
264
  with gr.Row():
265
  for c in range(MAX_GROUP):
 
267
  if z is None:
268
  gr.Button("", interactive=False)
269
  else:
270
+ sym = DF.loc[DF["Z"] == z, "symbol"].values[0]
271
  btn = gr.Button(sym)
272
+ btn.click(handle_button_click, inputs=[gr.Number(z, visible=False)],
273
+ outputs=[info, facts, trend])
274
 
275
  gr.Markdown("### f-block (lanthanides & actinides)")
276
  with gr.Row():
277
  for z in LAN:
278
+ sym = DF.loc[DF["Z"] == z, "symbol"].values[0]
279
  btn = gr.Button(sym)
280
+ btn.click(handle_button_click, inputs=[gr.Number(z, visible=False)],
281
+ outputs=[info, facts, trend])
282
  with gr.Row():
283
  for z in ACT:
284
+ sym = DF.loc[DF["Z"] == z, "symbol"].values[0]
285
  btn = gr.Button(sym)
286
+ btn.click(handle_button_click, inputs=[gr.Number(z, visible=False)],
287
+ outputs=[info, facts, trend])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
288
 
289
  if __name__ == "__main__":
290
  demo.launch()
291
+
292
+