我使用下面的代码来组合一堆csv文件。 [UPC]
列以000000
开头。 Pandas将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
之外,不需要建议不同的合并/组合方法。
答案 0 :(得分:3)
我认为您需要更改combine_first
并将参数dtype
添加到read_csv
字典 - 列名称str
。
同样,对于列名称和sch_inx
之间的交集,使用索引numpy.intersect1d
并选择相交的列:
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)
您不能将dtype
与index_col
放在pandas 0.22.0
中,因为bug。
答案 1 :(得分:3)
问题可能在于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
。
答案 2 :(得分:2)
第1点
有几种方法可以保留'[UPC]'
列的字符串。
dtype
converters
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]'
]
现在让我们展示三种转换的工作原理:
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
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
使用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点
如果你想要优雅,当pd.DataFrame.combine_first
已经是一个带有两个参数的函数时,不需要使用带有两个参数的lambda。此外,您可以将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
如何返回这些文件。除非你不在乎,否则......祝你好运。
答案 3 :(得分:0)
使用read_csv
时,可以通过传递dtype
参数来设置列的类型。例如:
pd.read_csv(f, index_col=sch_inx, dtype={'[UPC]': 'str'})
请参阅:docs