【问题标题】:Python mapping two csv filesPython映射两个csv文件
【发布时间】:2021-07-04 00:18:36
【问题描述】:

我有一个配置文件(csv):

Column name;Function;Args
Region;function1;arg1
Country;function2;arg1, arg2
email;function3;arg1
...

我想将我的配置文件中的特定功能应用到我的 csv 文件中的特定列(fileIn 大文件 > 1GB) 使用 dask、pandas 或标准 csv:

Region;Country;name
Europe;Slovakia;Mark
Asia;china;Steeve
...

有没有一种干净的方式来迭代配置文件?

df = pd.read_csv(fileIn, sep=';', low_memory=True, chunksize=1000000, error_bad_lines=False)

for chunk in df
    chunk['Region'] = chunk['Region'].apply(lambda x: MyClass.function1(args1))
    chunk['Country'] = chunk['Country'].apply(lambda x: MyClass.function2(arg1, arg2))
    chunk['email'] = chunk['email'].apply(lambda x: MyClass.function3(arg1))
    
    chunk['Region'].to_csv(fileOut, index=False, header=True, sep=';')
    ...


这是我在配置文件中调用的函数之一的示例:

def function1(value, replaceWith):
    text = re.sub(r'[^ ]', replaceWith, value)
    return text

【问题讨论】:

  • 您可以将dask 用于不适合内存的数据帧。
  • 使用read_csv方法pandas.pydata.org/docs/reference/api/pandas.read_csv.html的converters参数读取csv时可以将特定函数映射到列
  • @IvanCalderon 它适用于 pandas,但我有一个大文件,而且我读过很多文章表明 dask 比 pandas 更快。
  • @siraj 似乎 dask 为您完成了繁重的工作,因此您几乎可以像处理 pandas 数据框一样处理 dask 数据框。如果您已经安装了 dask,请检查 dd.read_csv 以发现它是否具有转换器参数 examples.dask.org/dataframes/…
  • @IvanCalderon,是的,这就是我想要做的事情:df = ddf.read_csv(fileIn, names='Region', low_memory=False) df = df.apply(function1(df, '*'), axis=1).compute()。我收到此错误:expected string or bytes-like object 因为我的function1 采用@ 参数:第一个是行值(字符串),第二个是'*'。有什么建议吗?

标签: python pandas dataframe csv dask


【解决方案1】:

您可以设置一个带有函数的字典,并在每次迭代时将其应用于块数据帧。

这里有一些代码,解释请看cmets:

# set up functions, for example
# - f1 to uppercase
# - f2 to lowercase
# - f3 to reverse string
def f1(x):
    return x.upper()

def f2(x):
    return x.lower()

def f3(x):
    return x[::-1]

# set up dict mapping function name in config to a function
fns = {
    'function1': f1,
    'function2': f2,
    'function3': f3,
}

# read config and set dict mapping column to a function
# here using the following `config.csv`:
#   Column name;Function
#   Region;function1
#   Country;function2
#   name;function3
df_config = pd.read_csv('config.csv', sep=';')
col_fns = df_config.set_index('Column name')['Function'].map(fns).to_dict()

# read and process csv in chunks
fileIn = 'file.csv'
fileOut = 'out.csv'
chunkSize = 1

df = pd.read_csv(
    fileIn, sep=';', low_memory=True, chunksize=chunkSize, error_bad_lines=False)

for i, chunk in enumerate(df):
    chunk_processed = chunk.apply(col_fns) # apply functions
    chunk_processed.to_csv(
        fileOut, index=False, header=(i==0), sep=';', mode='w' if i==0 else 'a')

# read the first 100 lines of the processed csv to test
pd.read_csv(fileOut, sep=';', nrows=100)

输出:

   Region   Country    name
0  EUROPE  slovakia    kraM
1    ASIA     china  eveetS

附:你需要把chunkSize改成合理的,当然这里只用1来测试,因为inFile只有2行,我们应该多块测试


更新:如果你需要应用的函数有一些参数,你可以创建包装函数:

# set up functions
def f(x, replaceWith):
    return re.sub(r'[^ ]', replaceWith, x)

# set up dict mapping function name in config to a function
fns = {
    'function1': f,
    'function2': f,
    'function3': f,
}

# read config and set dict mapping column to a function
# here using the following `config.csv`:
#   Column name;Function;args
#   Region;function1;A
#   Country;function2;B
#   name;function3;C
df_config = pd.read_csv('config.csv', sep=';')
col_fns = {r['Column name']: lambda x: fns[r['Function']](x, r['args'])
           for _, r in df_config.iterrows()}

...

# making it output both raw and processed values:
for i, chunk in enumerate(df):
    chunk_processed = chunk.apply(col_fns) # apply functions
    chunk_out = pd.concat(
        [chunk, chunk_processed.add_suffix('_processed')], axis=1)
    chunk_out.to_csv(
        fileOut, index=False, header=(i==0), sep=';', mode='w' if i==0 else 'a')

输出:

   Region   Country    name Region_processed Country_processed name_processed
0  Europe  Slovakia    Mark           AAAAAA          BBBBBBBB           CCCC
1    Asia     china  Steeve             AAAA             BBBBB         CCCCCC

【讨论】:

  • 我的函数包含多个参数,如何向我的函数添加参数?这是一个函数示例:def function1(value, replaceWith): text = re.sub(r'[^ ]', replaceWith, value) return text
  • 所有记录的replaceWith 值是否相同?如果是这种情况,只需创建一个接受单个值 x 的包装函数:f = lambda x: function1(x, 'your replaceWith value')
  • 哦,对了,不知何故错过了,请查看更新,我已将原始值的串联添加到处理后
  • 当然,您可以动态创建这些包装函数,例如使用col_fns = {r['Column name']: lambda x: fns[r['Function']](x, r['args']) for _, r in df_config.iterrows()}(假设fns 是将函数名称如function1 映射到实际函数的字典)
  • 如果您的配置文件的args 列中有arg1, arg2,它将被读取为字符串,而不是列表。它将作为字符串传递给函数。如果需要,您可以将此字符串拆分为一个列表,例如使用'arg1, arg2'.split(', '),这将为您提供['arg1', 'arg2']
猜你喜欢
  • 2018-11-23
  • 1970-01-01
  • 2021-10-28
  • 2015-03-31
  • 1970-01-01
  • 2020-03-18
  • 2012-07-31
  • 2012-08-12
  • 2023-03-04
相关资源
最近更新 更多