熊猫在合并文件时设置数据类型



我用下面的代码来组合一堆csv文件。有一列[UPC]000000开头。熊猫将 UPC 检测为数值,因此忽略所有前导零。

import pandas as pd
file_ptn = os.path.join('nielsen_sku_fact*.csv')
files = glob.glob(file_ptn)
sch_inx = [
'[All Markets]',
'[All Periods]',
'[UPC]'
]
df = reduce(lambda  left,right: pd.DataFrame.combine_first(left,right), [pd.read_csv(f,index_col=sch_inx) for f in files])

挑战在于需要将[UPC]设置为索引,以便将所有文件合并到同一架构中。我更喜欢使用combine_first方法来实现代码优雅的目的;因此,无需建议除combine_first以外的其他合并/合并方法。

也许问题出在index_col参数上,为什么不在读取 csv 后设置索引。

li = [pd.read_csv(f, dtype={d:object for d in sch_inx }).set_index(sch_inx)  for f in files] 
main_df = reduce(lambda  left,right: pd.DataFrame.combine_first(left,right),li)

让我们举一个例子来保留前导零,即

amount  donorID  recipientID  year
0     0100      101           11  2014
1     0200      101           11  2014
2     0500      101           21  2014
3     0200      102           21  2014
# Copy the above dataframe 
sch_ind = ['amount','donorID']
df = pd.read_clipboard(dtype={d:object for d in sch_ind}).set_index(sch_ind)
print(df)
recipientID  year
amount donorID                   
0100   101               11  2014
0200   101               11  2014
0500   101               21  2014
0200   102               21  2014

如果它适用于clipboard它也适用于csv

我认为您需要更改combine_first并将参数dtype添加到字典read_csv- 类型为str的列名。

也用于索引numpy.intersect1d列名和sch_inx之间的交集,并选择相交的列:

dfs = []
di = {d:str for d in sch_inx}
for fp in files:
df = pd.read_csv(fp, dtype=di)
#if want only first intersectioned column add [0]
#col = np.intersect1d(df.columns, sch_inx)[0]
col = np.intersect1d(df.columns, sch_inx)
dfs.append(df.set_index(col))
df = reduce(lambda left,right: left.combine_first(right), dfs)

你不能在熊猫0.22.0中将dtypeindex_col一起使用,因为bug。

第 1
点 有几种方法可以保留'[UPC]'列的字符串性。

  1. 使用其他帖子中提到的dtype
  2. 使用converters
  3. 之后使用pd.Series.str.zfill执行转换
设置


让我们从设置一些文件开始。 我正在使用Jupyter Notebook,我可以使用方便的%%writefile魔法。

%%writefile nielson_sku_fact01.csv
[All Markets],[All Periods],[UPC],A,B
1,2,0001,3,4
1,3,2000,7,8

%%writefile nielson_sku_fact02.csv
[All Markets],[All Periods],[UPC],C,D
1,4,0001,3,4
1,3,3000,7,8

%%writefile nielson_sku_fact03.csv
[All Markets],[All Periods],[UPC],B,D
1,4,0002,10,11
1,2,2000,8,8

让我们使用 OP 的代码来获取一些变量

import glob
import os
import pandas as pd
from functools import reduce
files = glob.glob('nielson_sku_fact*.csv')
sch_inx = [
'[All Markets]',
'[All Periods]',
'[UPC]'
]

现在让我们展示这三个转换是如何工作的:

  1. pd.read_csv('nielson_sku_fact01.csv', dtype={'[UPC]': str})

    [All Markets]  [All Periods] [UPC]  A  B
    0              1              2  0001  3  4
    1              1              3  2000  7  8
    
  2. pd.read_csv('nielson_sku_fact01.csv', converters={'[UPC]': str})

    [All Markets]  [All Periods] [UPC]  A  B
    0              1              2  0001  3  4
    1              1              3  2000  7  8
    
  3. 使用pd.Series.str.zfill

    pd.read_csv('nielson_sku_fact01.csv')['[UPC]'].astype(str).pipe(
    lambda s: s.str.zfill(s.str.len().max()))
    [All Markets]  [All Periods] [UPC]  A  B
    0              1              2  0001  3  4
    1              1              3  2000  7  8
    

第 2 点
如果你想要优雅,当 lambda 已经是一个接受两个参数的函数时,没有必要使用接受两个参数的 lambdapd.DataFrame.combine_first。 此外,您可以将map与准备好的阅读功能一起使用,使其美观干净:

def read(filename):
return pd.read_csv(
filename,
converters={'[UPC]': str}
).set_index(sch_inx)
reduce(pd.DataFrame.combine_first, map(read, files))
A     B    C     D
[All Markets] [All Periods] [UPC]                      
1             2             0001   3.0   4.0  NaN   NaN
2000   NaN   8.0  NaN   8.0
3             2000   7.0   8.0  NaN   NaN
3000   NaN   NaN  7.0   8.0
4             0001   NaN   NaN  3.0   4.0
0002   NaN  10.0  NaN  11.0

第 3 点我认为
您应该重新考虑使用pd.DataFrame.combine_first,因为glob的性质看起来您不能非常轻松地控制文件的顺序。 您可能会得到不可预知的结果,具体取决于glob返回这些文件的方式。 除非你不在乎,否则...祝你好运。

使用read_csv时,可以通过传递dtype参数来设置列的类型。例如:

pd.read_csv(f, index_col=sch_inx, dtype={'[UPC]': 'str'})

请参阅:文档

最新更新