import gradio as gr import pandas as pd import plotly.express as px import numpy as np from numpy.core.multiarray import zeros import math as M def upload_file(files): file_paths = [file.name for file in files] df = pd.read_excel(file_paths[0]) return file_paths,og_Data.update(value=df, visible=True), gr.update(visible=False), gr.update(visible=False), gr.update(visible=False) def plot(df, one, two, element, indice, mix_norm): if mix_norm==True: df[indice]=0 else: df[one]=0 df[two]=0 to_drop=[] for ii in range(2,df.shape[1],2): to_drop.append(df.columns[ii]) for col in to_drop: df=df.drop(col, axis=1) df2=df.transpose() header=df2.iloc[0] df2=df2[1:] for jj in range(0,len(header)): header[jj]=str(header[jj]) df2.columns=header df2.reset_index() plot=px.line(df2, x=df2.index,y=df2.columns, markers=True, title=f"Internal Normalization to {int(one)}{element}/{int(two)}{element}", labels={ "index":f'i{element}', "value":f"εi{element}", f"{df.columns[0]}": " " }) #plot.update_layout(modebar={'orientation': 'h'}) if mix_norm==True: plot=px.line(df2, x=df2.index,y=df2.columns, markers=True, title=f"Internal Normalization to {int(one)}{element}/{int(two)}{element}
Index Isotope: {indice}{element}", labels={ "index":f'i{element}', "value":f"εi{element}", f"{df.columns[0]}": " " }) #plot.update_layout(modebar={'orientation': 'h'}) plot.update_xaxes(tickvals=df2.index) return gr.update(value=plot, visible=True) def norm(df, i, j, u, v, m, p, mix_norm): df1= np.zeros_like(df) df2=pd.DataFrame(data=df) normalized= pd.DataFrame(df1) normalized.columns = df2.columns normalized[normalized.columns[0]]=df[df.columns[0]] if mix_norm==False: m=j p=v i_col= df2.columns[((df2.columns.get_loc(float(f'{i}'))+1))] #columns which correspond to errors on m,i,j,p,u and v j_col= df2.columns[((df2.columns.get_loc(float(f'{j}'))+1))] u_col= df2.columns[((df2.columns.get_loc(float(f'{u}'))+1))] v_col= df2.columns[((df2.columns.get_loc(float(f'{v}'))+1))] m_col= df2.columns[((df2.columns.get_loc(float(f'{m}'))+1))] p_col= df2.columns[((df2.columns.get_loc(float(f'{p}'))+1))] if mix_norm==False: df2[i]=0 df2[j]=0 for err in df2[i_col]: if err == "": err=0 for err in df2[j_col]: if err =="": err=0 elif mix_norm==True: df2[m]=0 for err in df2[m_col]: if err=="": err=0 for ii in range(1,df2.shape[1],2): k= df2.columns[ii] error_k=df2.columns[ii+1] md=(k-p)/(u-v) for jj in range(0,df2.shape[0]): if (df2[k].iloc[jj] == "") or (df2[p].iloc[jj] == "") or (df2[v].iloc[jj] == "") or (df2[u].iloc[jj] == ""): normalized[k][jj] = "" else: df2[k][jj]=float(df2[k][jj]); df2[p][jj]=float(df2[p][jj]); df2[v][jj]=float(df2[v][jj]); df2[u][jj]=float(df2[u][jj]) #indexing... normalized[k][jj]= df2[k][jj]-df2[p][jj]+((df2[v][jj]-df2[u][jj])*md) #epsilon transformation equation normalized[k][jj]=round(normalized[k][jj], 3) if (df2[error_k].iloc[jj]=="") or (df2[p_col].iloc[jj]=="") or (df2[v_col].iloc[jj]=="") or (df2[u_col].iloc[jj]==""): normalized[error_k][jj] = "" else: df2[error_k][jj]=float(df2[error_k][jj]); df2[p_col][jj]=float(df2[p_col][jj]); df2[v_col][jj]=float(df2[v_col][jj]); df2[u_col][jj]=float(df2[u_col][jj]);# indexing.. normalized[error_k][jj]=M.sqrt(((1)**2)*(df2[error_k][jj])**2 + ((-1)**2)*(df2[p_col][jj])**2 + (md**2)*(df2[v_col][jj])**2 + (md**2)*(df2[u_col][jj])**2) #error propagation normalized[error_k][jj]=round(normalized[error_k][jj], 3) return gr.update(value=normalized, visible=True), xx.update(visible=True) def write_xl(norm,u,v, element, mix_norm,p): if mix_norm==False: xl=norm.to_excel(f'{int(u)}.{int(v)} {element} Normalization.xlsx') return f'{int(u)}.{int(v)} {element} Normalization.xlsx' else: xl=norm.to_excel(f'{int(u)}.{int(v)} {element} Mixed({int(p)}) Normalization.xlsx') return f'{int(u)}.{int(v)} {element} Mixed({int(p)}) Normalization.xlsx' def mix_norm_click(bool): if bool==True: val=True else: val=False return gr.update(visible=val), gr.update(visible=val) def hide_intro(intro): if intro==1: return gr.update(value=0), gr.update(value="Example Formatting"),gr.update(visible=False) else: return gr.update(value=1), gr.update(value="Hide Formatting"),gr.update(visible=True) def search_sample_names(df): names=df.iloc[:, 0].tolist() name_count = {} updated_names = [] for name in names: if name in name_count: name_count[name] += 1 updated_names.append(f"{name}{name_count[name]:02}") else: name_count[name] = 0 updated_names.append(name) df.iloc[:, 0]=updated_names return gr.update(value=df) with gr.Blocks() as demo: format= '{text}' text_with_link=format.format gr.Markdown(f""" # Epsilon-value Transformation Interface {text_with_link(link='mailto:dan_razionale@brown.edu', text='Dan Razionale')} and Gerrit Budde; Department of Earth, Environmental and Planetary Sciences, Brown University, Providence, RI 02912, USA.""") gr.Markdown(f"""Last updated June 5th, 2023 :: {text_with_link(link='https://huggingface.co/spaces/dpraz/Epsilon_Transformation', text='Repository')} :: DOI:{text_with_link(link='https://doi.org/10.26300/waw0-e545 ', text='10.26300/waw0-e545')}""") with gr.Row(): with gr.Column(scale=1): a1=gr.Markdown(f""" # Introduction: This script automates the transformation of internally-normalized isotope data, as originally described by {text_with_link(link='http://dx.doi.org/10.1086/182628', text='McCulloch & Wasserburg (1978).')} It allows the re-calculation of ε-values for any element using alternative isotope pairs for the internal normalization. In using this script, you will generate an interactive plot and table with the re-normalized data, both of which are downloadable. Additionally, errors will be propagated following the formula given on the right. However, this formula does not account for potential error correlations. Further, errors on isotopes used for internal normalization (i and j) are often assumed to be zero, which underestimates the true propagated error on some transformed data. Here, you have the option of entering non-zero values to obtain more accurate estimates of the propagated errors. If left blank, propagated errors whose calculations require these values will also appear blank.""") with gr.Row(): hide=gr.Button(value="Example Formatting").style(size="sm", full_width=False) with gr.Column(scale=1): a2=gr.Markdown("""# Calculations:""") with gr.Row(): eq1=gr.Image("Epsi_tran_calc.png", label="Epsilon Transformation:").style(height=75, width=350) with gr.Row(): eq2=gr.Image("Error_prop_calculation.png", label="Error Propagation:").style(height=100, width=400) with gr.Row(): with gr.Column(scale=1): a3=gr.Markdown(""" # Formatting Guidelines: To begin, assemble an excel document containing the data you wish to transform and save it in an easily accessible location. The functionality of this application hinges upon proper formatting of the input data – please adhere closely to the format defined in the annotated image. As an example, we provide a molybdenum dataset that is internally normalized to 98Mo/96Mo, to be transformed to a 97Mo/95Mo normalization.""") with gr.Column(scale=1): a4=gr.Markdown("k denotes the identity of the isotope of interest; u/v and i/j represent the new and old isotope pair used for internal normalization, respectively; p and m are the new and old index isotopes, respectively, which are the denominators in the isotope ratios. Usually, p=v and m=j, although ‘mixed normalizations’ are permitted by activating the homonymous checkbox, which allows you to manually set these indices. Where these variables are not preceded by an epsilon sign, they simply refer to the mass number of the respective isotopes; where they are preceded by a delta, they refer to the associated absolute error. ") with gr.Row(): format_img=gr.Image("Formatting.png", show_label=False, visible=False).style(height=400, width=750) with gr.Row(): intro=gr.Number(value=0, visible=False) hide.click(fn=hide_intro, inputs=[intro], outputs=[intro,hide,format_img]) with gr.Row(): with gr.Column(scale=1, min_width=100): Element= gr.Textbox(label="Element", placeholder="Enter element symbol (e.g. Mo)") with gr.Row(): mix_norm=gr.Checkbox(label="Mixed Normalization") with gr.Row(): upload_button = gr.UploadButton("Import Data", file_types=[".xlsx", ".xls",".csv"], file_count=1) with gr.Row(): norm_button=gr.Button("Transform and Plot") with gr.Column(scale=1, min_width=100): i= gr.Number(label="Current Isotope Pair for Internal Normalization [i/j]", info="e.g. for 98Mo/96Mo normalization", value=98) j= gr.Number(label="", value=96) m=gr.Number(visible=False, label= "Current Index Isotope [m]", value=96, info= "e.g. for kMo/96Mo ratios") with gr.Column(scale=1, min_width=100): u=gr.Number(label="Desired Isotope Pair for Internal Normalization [u/v]", info="e.g. for 97Mo/95Mo normalization", value=97) v=gr.Number(label="", value=95) p=gr.Number(visible=False, label= "Desired Index Isotope [p]", value=95, info= "e.g. for kMo/95Mo ratios") mix_norm.change(fn=mix_norm_click, inputs=[mix_norm], outputs=[m, p]) with gr.Row(): file_output = gr.File(visible=False) with gr.Row(): og_Data = gr.Dataframe(type="pandas", visible=False, label="Original Data", interactive=True) #### with gr.Row(): with gr.Column(): og_Plot= gr.Plot(label="Original Data", visible=False) with gr.Column(): norm_Plot= gr.Plot(label="Transformed Data", visible=False) with gr.Row(): norm_Data=gr.Dataframe(type="pandas", visible=False, label="Transformed data", interactive=False) with gr.Row(): with gr.Column(scale=3): gr.Button(visible=False,interactive=False) with gr.Column(scale=3): gr.Button(visible=False,interactive=False) xx=gr.Files(visible=False, file_count="multiple", label="Download .xlsx Files") with gr.Column(scale=1.1): upload_button.upload(upload_file, upload_button, outputs=[ file_output,og_Data,norm_Plot,og_Plot,norm_Data]).success(fn=search_sample_names, inputs=[og_Data], outputs=[og_Data]) norm_button.click(fn=search_sample_names, inputs=[og_Data],outputs=[og_Data]).success( fn=norm,inputs=[og_Data, i, j, u, v, m, p, mix_norm],outputs=[norm_Data, xx], scroll_to_output=True).success(fn=plot, inputs=[og_Data, i, j, Element, m, mix_norm], outputs=og_Plot, scroll_to_output=True).success( fn=plot, inputs=[norm_Data, u, v, Element, p, mix_norm], outputs=norm_Plot).success(fn=write_xl, inputs=[norm_Data, u, v, Element, mix_norm,p], outputs=[xx]) demo.queue(concurrency_count=5).launch(debug=True)