我用下面的代码来组合一堆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
中将dtype
与index_col
一起使用,因为bug。
第 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 点
如果你想要优雅,当 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'})
请参阅:文档