【问题标题】:openpyxl: Formulas read as blanks in some (key use) casesopenpyxl:在某些(关键用例)案例中,公式读作空白
【发布时间】:2016-10-19 08:19:38
【问题描述】:

我的代码从谷歌驱动器下载一个 .xlsx 文件(使用 pydrive),找到一些带有 pandas 的空白单元格,并用 openpyxl 填充这些空白单元格。

当我打开 openpyxl 更改文件时,一切看起来都很棒。但是,当我使用 pandas read_excel 函数时,所有具有方程式的单元格都被读取为空白。我怀疑问题出在 openpyxl 上,因为当我在驱动器上预览文件时,这些单元格是空白的。 openpyxl 没有触及的文件没有问题。

看起来我的问题与this one 非常相似,但由于我的目标只是保持公式不变(我只想填充空白单元格),我真的不想解析公式并且我'我不确定如何或是否应用Felipe's 修复。

我希望能够下载文件以使用散景绘制它,并且用户和 python 都将编辑程序,所以我真的希望 pandas 能够读取用户修改的方程文件或 openpyxl 修改的文件。文件中的方程式是单击并拖动“共享方程式”,如果可能的话,我想保持这种状态,所以理想情况下我想避免使用data_only=True。我尝试指定data_only=False,但这似乎并没有改变任何东西。

我使用的是openpyxl 2.3.5 2.4,并且在代码运行时我保持关闭excel。

openpyxl修改前后文件版本为available here

我的代码在这里,所有 openpyxl 代码都被隔离到: # 导入库 导入日期时间 进口小鬼 导入操作系统 将熊猫导入为 pd 从 openpyxl 导入 load_workbook 从 itertools 导入 islice # 散景交互的相对导入

dl = imp.load_source('downloader', os.getcwd() +
                      '/Project/downloader.py')
gdu = imp.load_source('googledriveutils', os.getcwd() +
                      '/Project/googledriveutils.py')
remove_file = gdu.remove_file
find_folderid = gdu.find_folderid
get_file_list = gdu.get_file_list


# Define constants
COL_LABEL = '\nProbe - '
# TODO: ORP PROBE: REVISE THIS DATE when orp probe is added
IGNORE_BEFORE = pd.to_datetime('5.24.2016')
PROBE_DICT = {'DO (mg/L)': 'DO mg/L',
              'pH': 'pH',
              'NH4+ (mgN/L)': 'Ammonium',
              'ORP (mV)': 'ORP mV'}
TS = '\nTimestamps'


def save_to_workbook(newval,
                     date,
                     header,
                     rows_to_skip=12,
                     wbname='temp.xlsx',
                     sheet_name='Reactor Data'):
    wb = load_workbook(wbname)
    ws = wb[sheet_name]
    for cell in ws[rows_to_skip+1]:
        # TODO: Error if header isn't found
        if cell.value == header:
            colno = cell.col_idx
            break

    for row in ws.iter_rows(min_row=rows_to_skip+1, min_col=1, max_col=1):
        for cell in row:
        # TODO: Error if date isn't found
            if cell.value == date:
                rowno = cell.row
                break

    ws.cell(row=rowno, column=colno).value = newval
    wb.save(wbname)


    return df





def find_r1masterfile():
    # Navigate through the directories
    wlab_fid = find_folderid('Winkler Lab', 'root')
    kp_fid = find_folderid('KathrynsProjects', wlab_fid)
    amxrct_fid = find_folderid('Anammox Reactor', kp_fid)
    trials_fid = find_folderid('Reactor Trials', amxrct_fid)
    # List files in directory
    file_list = get_file_list(trials_fid)
    for afile in file_list:
        if afile['title'] == 'AMX RCT.xlsx':
            # Return the file we asked for
                return afile
        # TODO: error if there was no file with that name


def save_r1masterfile(csv, rows_to_skip=12, filename='temp.xlsx', sheet_name='Reactor Data'):
    # Get the file we want
    master_file = find_r1masterfile()
    try:
        master_file.GetContentFile(filename)
    except Exception, e:
        print "Warning: Something wrong with file R1 Master File."
        print str(e)
        # TODO: add an email alarm to responsible user

    if csv:
        return master_file
    else:
        # convert to dataframe
        wb = load_workbook(filename, data_only=True)
        ws = wb[sheet_name]
        print ws["B14"].value
        data = ws.values
        data = list(data)[rows_to_skip:]
        cols = list(data[0])
        del cols[0]
        del data[0]
        idx = [r[0] for r in data]
        data = (islice(r, 1, None) for r in data)
        df = pd.DataFrame(data, index=idx, columns=cols)
        print df.dropna(how='all')
        remove_file(filename)
        return df


def upload_r1masterfile(filename='temp.xlsx'):
    # Get the file we want
    master_file = find_r1masterfile()
    try:
        master_file.SetContentFile(filename)
        master_file.Upload()
    except Exception, e:
        print "Warning: Something wrong with file R1 Master File."
        print str(e)
        # TODO: add an email alarm to responsible user


def populate_r1masterfile(rows_to_skip=12, filename='temp.xlsx'):
    # Get the R1 master file as a file
    save_r1masterfile(True)
    # Convert the juicy stuff to a dataframe
    masterdf = pd.read_excel(filename,
                             sheetname='Reactor Data',
                             encoding="utf-16",
                             skiprows=rows_to_skip,
                             sep='\t',
                             index_col='Date',
                             keep_default_na=False,
                             na_values=['-1.#IND', '1.#QNAN', '1.#IND',
                             '-1.#QNAN', '','N/A', '#NA', 'NA'
                             'NULL', 'NaN', '-NaN', 'nan', '-nan'])
    # Find what we will populate with probe data
    # Find timestamps
    ts_columns = [col for col in masterdf.columns if TS in col]
    tsdf = masterdf[ts_columns]
    # Find probes, ignore before given date
    probe_columns = [col for col in masterdf.columns if COL_LABEL in col]
    probedf = masterdf[probe_columns]
    probedf = probedf[masterdf.index > IGNORE_BEFORE]
    # Find Indices and column labels of blank values
    stackdf = probedf.stack(dropna=False)
    empty = stackdf[stackdf.isnull()].index.tolist()

    # For each blank look for the probe, time & date of cycle, and return val
    for each in empty:
        probe, time = each[1].split(COL_LABEL)
        time = tsdf.loc[each[0], time+TS]
        ts = each[0]+pd.DateOffset(hour=time.hour, minute=time.minute)
        val = dl.get_val_from(1, ts, PROBE_DICT.get(probe))
        probedf.set_value(each[0], each[1], val)
        # Save that value to the workbook
        save_to_workbook(val, each[0], each[1])
    upload_r1masterfile()
    print 'Master file updated. ' + str(datetime.datetime.now())
    remove_file('temp.xlsx')
    return probedf

更新

我根据查理的建议修改了我的代码(上面更新了)。但我仍然在数据框中得到无。举个更具体的例子,我运行这段代码的时候为什么会这样:

from openpyxl import load_workbook

wb = load_workbook('AMX RCT mod.xlsx', data_only=True)
ws = wb['Reactor Data']
print 'Value of B14 Formula is: ' + str(ws["B14"].value)

有了this file,我回来了?:

Value of B14 Formula is: None

有解决办法吗?

【问题讨论】:

  • 您能否提供一个更具体的示例来说明您的意思,并可能提供一个示例文件? 2.3包含Felipe的公式翻译代码。但是,如果您想使用 Pandas,您可能需要尝试 2.4,它可以让您直接从工作表转到数据框。

标签: python pandas openpyxl


【解决方案1】:

使用 openpyxl 2.4,您或许可以一次性完成您需要的工作。我已经采用了你的第一个函数并对其进行了调整。

from itertools import islice
from pandas import DataFrame

def save_to_workbook(newval,
                     date,
                     header,
                     rows_to_skip=12,
                     wbname='temp.xlsx',
                     sheet_name='Reactor Data'):
    wb = load_workbook(wbname)
    ws = wb[sheet_name]
    rowno = None
    colno = None
    for cell in ws[1]:
        # TODO: Error if header isn't found
        if cell.value == header:
            colno = col

    for row in ws.iter_rows(min_row=rows_to_skip+1, min_col=1, max_col=1):
        for cell in row:
        # TODO: Error if date isn't found
            if cell.value == date:
                rowno = row
                break

    # TODO: Fix this
    ws.cell(row=rowno, column=colno).value = newval

    # convert to dataframe
    data = ws.values
    cols = next(data)[1:]
    data = list(data)
    idx = [r[0] for r in data]
    data = (islice(r, 1, None) for r in data)
    df = DataFrame(data, index=idx, columns=cols)

    return df

这可能无法满足您的所有需求,但有望帮助您入门。它还避免了保存和解析整个工作簿,这可以使它更快。

要使用 openpyxl 2.4,您需要执行 pip install -U --pre openpyxl 或使用结帐。

有关同时使用 openpyxl 和 pandas 的更多信息,请参阅documentation

【讨论】:

  • 嗨查理!非常感谢您的回复。这确实帮助我想到了一些更好的方法来构建我的代码。但是,当我用公式请求一个值时,我“无”回来了。还有更多提示吗?
  • er,你在哪里“用公式求值”?我建议这在邮件列表中可能更有意义,如果您可以提供文件。
  • 发送到邮件列表。澄清一下,当您说“提供文件”时,您是指我的 .py 文件还是我的 .xlsx 文件?
  • 无论您拥有什么并且可以分享。对我提出的“编写我的代码”请求遭到严厉反对。
  • 很高兴为未来的谷歌员工分享我所拥有的一切:上面链接的 git repo 上提供的一切。
【解决方案2】:

mailing list查理的回答:

因此,如果您想保留公式,则不能使用仅数据模式。
如前所述,openpyxl 从不计算公式,所以如果你想
知道 A3 的值,您必须将文件传递给 Excel 等应用程序
或 OpenOffice — 您可以无头运行 OpenOffice 来处理这类事情或
将 xlwings 用于 Excel — 确实可以进行公式评估。然后你可以
以仅数据模式读取此文件以查看计算结果。
或者,您可以尝试使用 PyCel 之类的东西来执行
为你评估。但是,基本上如果你想做计算:做他们
在 Python 中。

根据他的建议,我的解决方法是逐列重做所有计算,就像在 excel 文件中完成的那样。 IE。对于这样的excel文件:

        col1 col2   col3    col4
row1    1    3      =A1+B1  =1+3
row2    2    4      =A2+B2  =2+4

我将它作为这样的数据框导入(以将方程式维护为字符串):

wb = load_workbook(filename, data_only=False)
ws = wb[sheet_name]
data = ws.values
cols = next(data)[1:]
data = list(data)
idx = [r[0] for r in data]
data = (islice(r, 1, None) for r in data)
df = DataFrame(data, index=idx, columns=cols)

然后执行以下操作:

parse_excel = lambda x: eval(str(x)[1:]) if isinstance(x, str) else x
for col in df.columns:
    try:
        df[col] = df[col].map(parse_excel)
    except:
        pass
df['col3'] = df['col2']+df['col1']

我敢肯定,这可能是最笨拙的方法,但目前可行。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-19
    • 1970-01-01
    • 1970-01-01
    • 2015-05-26
    • 2020-06-12
    相关资源
    最近更新 更多