Vishwas1 commited on
Commit
1453284
·
verified ·
1 Parent(s): 933da9e

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +73 -39
app.py CHANGED
@@ -7,6 +7,23 @@ import numpy as np
7
  import matplotlib.pyplot as plt
8
  from periodictable import elements
9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
  # -----------------------------
11
  # Data
12
  # -----------------------------
@@ -65,23 +82,23 @@ def classify_category(el) -> str:
65
  return "alkali"
66
  if el.block == "s" and el.group == 2:
67
  return "alkaline-earth"
68
- if el.block == "p" and el.group in (13, 14, 15, 16) and el.metallic:
69
- return "post-transition"
70
- if el.block == "d":
71
- return "transition"
72
  if el.block == "p" and el.group == 17:
73
  return "halogen"
74
  if el.block == "p" and el.group == 18:
75
  return "noble-gas"
76
- if el.block == "p" and not el.metallic:
77
- return "nonmetal"
78
  if el.block == "f" and 57 <= el.number <= 71:
79
  return "lanthanide"
80
  if el.block == "f" and 89 <= el.number <= 103:
81
  return "actinide"
 
 
 
 
82
  except Exception:
83
  pass
84
- return "nonmetal" if not getattr(el, "metallic", False) else "post-transition"
85
 
86
  def build_elements_df() -> pd.DataFrame:
87
  rows = []
@@ -94,45 +111,64 @@ def build_elements_df() -> pd.DataFrame:
94
  "symbol": el.symbol,
95
  "name": el.name.title(),
96
  "period": getattr(el, "period", None),
97
- "group": getattr(el, "group", None),
98
  "block": getattr(el, "block", None),
99
- "mass": getattr(el, "mass", None),
100
- "density": getattr(el, "density", None),
101
- "electronegativity": getattr(el, "electronegativity", None),
102
- "boiling_point": getattr(el, "boiling_point", None),
103
- "melting_point": getattr(el, "melting_point", None),
104
- "vdw_radius": getattr(el, "vdw_radius", None),
105
- "covalent_radius": getattr(el, "covalent_radius", None),
106
  "category": classify_category(el),
107
  "is_radioactive": bool(getattr(el, "radioactive", False)),
108
  }
109
  rows.append(data)
110
- df = pd.DataFrame(rows).sort_values("Z").reset_index(drop=True)
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
- if pd.isna(period) or group is None:
 
 
 
 
 
 
 
127
  continue
128
- period = int(period)
129
- GRID[period - 1][group - 1] = z
 
 
 
 
 
 
 
 
 
 
130
 
 
131
  LAN = [int(z) for z in DF["Z"] if 57 <= int(z) <= 71]
132
  ACT = [int(z) for z in DF["Z"] if 89 <= int(z) <= 103]
133
 
134
  # -----------------------------
135
- # Plotting helpers (Matplotlib -> gr.Plot)
136
  # -----------------------------
137
  def plot_trend(trend_df: pd.DataFrame, prop_key: str, Z: int, symbol: str):
138
  fig, ax = plt.subplots()
@@ -198,13 +234,13 @@ def element_info(z_or_symbol: str):
198
  f"Period {int(row['period']) if not pd.isna(row['period']) else '—'}, "
199
  f"Group {row['group'] if row['group'] is not None else '—'}, "
200
  f"Block {row['block']} | Category: {row['category'].replace('-', ' ').title()}",
201
- f"Atomic mass: {row['mass'] if row['mass'] else '—'} u",
202
- f"Density: {row['density'] if row['density'] else '—'} g/cm³",
203
- f"Electronegativity: {row['electronegativity'] if row['electronegativity'] else '—'} (Pauling)",
204
- f"Melting point: {row['melting_point'] if row['melting_point'] else '—'} K | "
205
- f"Boiling point: {row['boiling_point'] if row['boiling_point'] else '—'} K",
206
- f"vdW radius: {row['vdw_radius'] if row['vdw_radius'] else '—'} pm | "
207
- f"Covalent radius: {row['covalent_radius'] if row['covalent_radius'] else '—'} pm",
208
  f"Radioactive: {'Yes' if row['is_radioactive'] else 'No'}",
209
  ]
210
  info_text = "\n".join(props_lines)
@@ -226,7 +262,7 @@ def search_element(query: str):
226
  return element_info(query)
227
 
228
  # -----------------------------
229
- # UI (compatible with Gradio 4.29.0)
230
  # -----------------------------
231
  with gr.Blocks(title="Interactive Periodic Table") as demo:
232
  gr.Markdown("# 🧪 Interactive Periodic Table\nClick an element or search by symbol/name/atomic number.")
@@ -239,24 +275,21 @@ with gr.Blocks(title="Interactive Periodic Table") as demo:
239
  info = gr.Textbox(label="Properties", lines=10, interactive=False)
240
  facts = gr.Markdown("Select an element to see fun facts.")
241
  trend = gr.Plot()
242
-
243
  search.submit(search_element, inputs=[search], outputs=[info, facts, trend])
244
 
245
  gr.Markdown("### Trend heatmap")
246
  prop = gr.Dropdown(choices=[k for k, _ in NUMERIC_PROPS], value="electronegativity", label="Property")
247
  heat = gr.Plot()
248
  prop.change(lambda k: plot_heatmap(k), inputs=[prop], outputs=[heat])
249
-
250
- # Initialize heatmap on load
251
  demo.load(lambda: plot_heatmap("electronegativity"), outputs=[heat])
252
 
253
- # Grid of element buttons
254
  with gr.Column(scale=2):
255
  gr.Markdown("### Main Table")
 
256
  with gr.Row():
257
  for g in range(1, 19):
258
  gr.Markdown(f"**{g}**")
259
-
260
  for r in range(MAX_PERIOD):
261
  with gr.Row():
262
  for c in range(MAX_GROUP):
@@ -285,3 +318,4 @@ with gr.Blocks(title="Interactive Periodic Table") as demo:
285
 
286
  if __name__ == "__main__":
287
  demo.launch()
 
 
7
  import matplotlib.pyplot as plt
8
  from periodictable import elements
9
 
10
+ # -----------------------------
11
+ # Helpers
12
+ # -----------------------------
13
+ def to_float(x):
14
+ """Coerce periodictable numeric (incl. uncertainties) to plain float, else NaN."""
15
+ if x is None:
16
+ return np.nan
17
+ try:
18
+ # uncertainties.UFloat has .nominal_value
19
+ v = getattr(x, "nominal_value", x)
20
+ return float(v)
21
+ except Exception:
22
+ try:
23
+ return float(x)
24
+ except Exception:
25
+ return np.nan
26
+
27
  # -----------------------------
28
  # Data
29
  # -----------------------------
 
82
  return "alkali"
83
  if el.block == "s" and el.group == 2:
84
  return "alkaline-earth"
 
 
 
 
85
  if el.block == "p" and el.group == 17:
86
  return "halogen"
87
  if el.block == "p" and el.group == 18:
88
  return "noble-gas"
89
+ if el.block == "d":
90
+ return "transition"
91
  if el.block == "f" and 57 <= el.number <= 71:
92
  return "lanthanide"
93
  if el.block == "f" and 89 <= el.number <= 103:
94
  return "actinide"
95
+ if el.block == "p" and not el.metallic:
96
+ return "nonmetal"
97
+ if el.block == "p" and el.metallic:
98
+ return "post-transition"
99
  except Exception:
100
  pass
101
+ return "post-transition" if getattr(el, "metallic", False) else "nonmetal"
102
 
103
  def build_elements_df() -> pd.DataFrame:
104
  rows = []
 
111
  "symbol": el.symbol,
112
  "name": el.name.title(),
113
  "period": getattr(el, "period", None),
114
+ "group": getattr(el, "group", None), # may be None for many
115
  "block": getattr(el, "block", None),
116
+ "mass": to_float(getattr(el, "mass", None)),
117
+ "density": to_float(getattr(el, "density", None)),
118
+ "electronegativity": to_float(getattr(el, "electronegativity", None)),
119
+ "boiling_point": to_float(getattr(el, "boiling_point", None)),
120
+ "melting_point": to_float(getattr(el, "melting_point", None)),
121
+ "vdw_radius": to_float(getattr(el, "vdw_radius", None)),
122
+ "covalent_radius": to_float(getattr(el, "covalent_radius", None)),
123
  "category": classify_category(el),
124
  "is_radioactive": bool(getattr(el, "radioactive", False)),
125
  }
126
  rows.append(data)
127
+ return pd.DataFrame(rows).sort_values("Z").reset_index(drop=True)
 
128
 
129
  DF = build_elements_df()
130
 
131
  # -----------------------------
132
+ # Build a robust grid (no reliance on group from the lib)
133
+ # Rules: s->groups 1-2, d->3..12, p->13..18; period 1 special (H at 1, He at 18)
134
+ # f-block shown separately.
135
  # -----------------------------
136
  MAX_GROUP = 18
137
  MAX_PERIOD = 7
138
  GRID: List[List[Optional[int]]] = [[None for _ in range(MAX_GROUP)] for _ in range(MAX_PERIOD)]
139
 
140
+ for period in range(1, MAX_PERIOD + 1):
141
+ rows = DF[DF["period"] == period].sort_values("Z")
142
+ s = rows[rows["block"] == "s"]["Z"].tolist()
143
+ d = rows[rows["block"] == "d"]["Z"].tolist()
144
+ p = rows[rows["block"] == "p"]["Z"].tolist()
145
+ # Period 1 special case
146
+ if period == 1:
147
+ # Expect H then He
148
+ if len(s) >= 1:
149
+ GRID[0][0] = int(s[0]) # H -> group 1
150
+ if len(p) >= 1:
151
+ GRID[0][17] = int(p[-1]) # He -> group 18
152
  continue
153
+ # s-block (usually 2)
154
+ if len(s) >= 1:
155
+ GRID[period - 1][0] = int(s[0]) # group 1
156
+ if len(s) >= 2:
157
+ GRID[period - 1][1] = int(s[1]) # group 2
158
+ # d-block (10 wide), only in periods >= 4
159
+ for i, z in enumerate(d):
160
+ if i < 10:
161
+ GRID[period - 1][2 + i] = int(z) # groups 3..12
162
+ # p-block (6 wide)
163
+ for i, z in enumerate(p[-6:]): # last 6 p-block in order
164
+ GRID[period - 1][12 + i] = int(z) # groups 13..18
165
 
166
+ # f-block lists (lanthanides/actinides)
167
  LAN = [int(z) for z in DF["Z"] if 57 <= int(z) <= 71]
168
  ACT = [int(z) for z in DF["Z"] if 89 <= int(z) <= 103]
169
 
170
  # -----------------------------
171
+ # Plotting (Matplotlib -> gr.Plot)
172
  # -----------------------------
173
  def plot_trend(trend_df: pd.DataFrame, prop_key: str, Z: int, symbol: str):
174
  fig, ax = plt.subplots()
 
234
  f"Period {int(row['period']) if not pd.isna(row['period']) else '—'}, "
235
  f"Group {row['group'] if row['group'] is not None else '—'}, "
236
  f"Block {row['block']} | Category: {row['category'].replace('-', ' ').title()}",
237
+ f"Atomic mass: {row['mass'] if not pd.isna(row['mass']) else '—'} u",
238
+ f"Density: {row['density'] if not pd.isna(row['density']) else '—'} g/cm³",
239
+ f"Electronegativity: {row['electronegativity'] if not pd.isna(row['electronegativity']) else '—'} (Pauling)",
240
+ f"Melting point: {row['melting_point'] if not pd.isna(row['melting_point']) else '—'} K | "
241
+ f"Boiling point: {row['boiling_point'] if not pd.isna(row['boiling_point']) else '—'} K",
242
+ f"vdW radius: {row['vdw_radius'] if not pd.isna(row['vdw_radius']) else '—'} pm | "
243
+ f"Covalent radius: {row['covalent_radius'] if not pd.isna(row['covalent_radius']) else '—'} pm",
244
  f"Radioactive: {'Yes' if row['is_radioactive'] else 'No'}",
245
  ]
246
  info_text = "\n".join(props_lines)
 
262
  return element_info(query)
263
 
264
  # -----------------------------
265
+ # UI (Gradio 4.29.0 compatible)
266
  # -----------------------------
267
  with gr.Blocks(title="Interactive Periodic Table") as demo:
268
  gr.Markdown("# 🧪 Interactive Periodic Table\nClick an element or search by symbol/name/atomic number.")
 
275
  info = gr.Textbox(label="Properties", lines=10, interactive=False)
276
  facts = gr.Markdown("Select an element to see fun facts.")
277
  trend = gr.Plot()
 
278
  search.submit(search_element, inputs=[search], outputs=[info, facts, trend])
279
 
280
  gr.Markdown("### Trend heatmap")
281
  prop = gr.Dropdown(choices=[k for k, _ in NUMERIC_PROPS], value="electronegativity", label="Property")
282
  heat = gr.Plot()
283
  prop.change(lambda k: plot_heatmap(k), inputs=[prop], outputs=[heat])
 
 
284
  demo.load(lambda: plot_heatmap("electronegativity"), outputs=[heat])
285
 
 
286
  with gr.Column(scale=2):
287
  gr.Markdown("### Main Table")
288
+ # Group headers (1..18)
289
  with gr.Row():
290
  for g in range(1, 19):
291
  gr.Markdown(f"**{g}**")
292
+ # Grid of element buttons
293
  for r in range(MAX_PERIOD):
294
  with gr.Row():
295
  for c in range(MAX_GROUP):
 
318
 
319
  if __name__ == "__main__":
320
  demo.launch()
321
+