4. concat, join, 和merge的区别

concat

  • Pandas函数
  • 可以垂直和水平地连接两个或多个pandas对象
  • 只用索引对齐
  • 索引出现重复值时会报错
  • 默认是外连接(也可以设为内连接)

join

  • DataFrame方法
  • 只能水平连接两个或多个pandas对象
  • 对齐是靠被调用的DataFrame的列索引或行索引和另一个对象的行索引(不能是列索引)
  • 通过笛卡尔积处理重复的索引值
  • 默认是左连接(也可以设为内连接、外连接和右连接)

merge

  • DataFrame方法
  • 只能水平连接两个DataFrame对象
  • 对齐是靠被调用的DataFrame的列或行索引和另一个DataFrame的列或行索引
  • 通过笛卡尔积处理重复的索引值
  • 默认是内连接(也可以设为左连接、外连接、右连接)
  1. # 用户自定义的display_frames函数,可以接收一列DataFrame,然后在一行中显示:
  2. In[91]: from IPython.display import display_html
  3. years = 2016, 2017, 2018
  4. stock_tables = [pd.read_csv('data/stocks_{}.csv'.format(year), index_col='Symbol')
  5. for year in years]
  6. def display_frames(frames, num_spaces=0):
  7. t_style = '<table style="display: inline;"'
  8. tables_html = [df.to_html().replace('<table', t_style) for df in frames]
  9. space = '&nbsp;' * num_spaces
  10. display_html(space.join(tables_html), raw=True)
  11. display_frames(stock_tables, 30)
  12. stocks_2016, stocks_2017, stocks_2018 = stock_tables

4. concat, join, 和merge的区别 - 图1

  1. # concat是唯一一个可以将DataFrames垂直连接起来的函数
  2. In[92]: pd.concat(stock_tables, keys=[2016, 2017, 2018])
  3. Out[92]:

4. concat, join, 和merge的区别 - 图2

  1. # concat也可以将DataFrame水平连起来
  2. In[93]: pd.concat(dict(zip(years,stock_tables)), axis='columns')
  3. Out[93]:

4. concat, join, 和merge的区别 - 图3

  1. # 用join将DataFrame连起来;如果列名有相同的,需要设置lsuffix或rsuffix以进行区分
  2. In[94]: stocks_2016.join(stocks_2017, lsuffix='_2016', rsuffix='_2017', how='outer')
  3. Out[94]:

4. concat, join, 和merge的区别 - 图4

  1. In[95]: stocks_2016
  2. Out[95]:

4. concat, join, 和merge的区别 - 图5

  1. # 要重现前面的concat方法,可以将一个DataFrame列表传入join
  2. In[96]: other = [stocks_2017.add_suffix('_2017'), stocks_2018.add_suffix('_2018')]
  3. stocks_2016.add_suffix('_2016').join(other, how='outer')
  4. Out[96]:

4. concat, join, 和merge的区别 - 图6

  1. # 检验这两个方法是否相同
  2. In[97]: stock_join = stocks_2016.add_suffix('_2016').join(other, how='outer')
  3. stock_concat = pd.concat(dict(zip(years,stock_tables)), axis='columns')
  4. In[98]: stock_concat.columns = stock_concat.columns.get_level_values(1) + '_' + \
  5. stock_concat.columns.get_level_values(0).astype(str)
  6. In[99]: stock_concat
  7. Out[99]:

4. concat, join, 和merge的区别 - 图7

  1. In[100]: step1 = stocks_2016.merge(stocks_2017, left_index=True, right_index=True,
  2. how='outer', suffixes=('_2016', '_2017'))
  3. stock_merge = step1.merge(stocks_2018.add_suffix('_2018'),
  4. left_index=True, right_index=True, how='outer')
  5. stock_concat.equals(stock_merge)
  6. Out[100]: True
  1. # 查看food_prices和food_transactions两个小数据集
  2. In[101]: names = ['prices', 'transactions']
  3. food_tables = [pd.read_csv('data/food_{}.csv'.format(name)) for name in names]
  4. food_prices, food_transactions = food_tables
  5. display_frames(food_tables, 30)

4. concat, join, 和merge的区别 - 图8

  1. # 通过键item和store,将food_transactions和food_prices两个数据集融合
  2. In[102]: food_transactions.merge(food_prices, on=['item', 'store'])
  3. Out[102]:

4. concat, join, 和merge的区别 - 图9

  1. # 因为steak在两张表中分别出现了两次,融合时产生了笛卡尔积,造成结果中出现了四行steak;因为coconut没有对应的价格,造成结果中没有coconut
  2. # 下面只融合2017年的数据
  3. In[103]: food_transactions.merge(food_prices.query('Date == 2017'), how='left')
  4. Out[103]:

4. concat, join, 和merge的区别 - 图10

  1. # 使用join复现上面的方法,需要需要将要连接的food_prices列转换为行索引
  2. In[104]: food_prices_join = food_prices.query('Date == 2017').set_index(['item', 'store'])
  3. food_prices_join
  4. Out[104]:

4. concat, join, 和merge的区别 - 图11

  1. # join方法只对齐传入DataFrame的行索引,但可以对齐调用DataFrame的行索引和列索引;
  2. # 要使用列做对齐,需要将其传给参数on
  3. In[105]: food_transactions.join(food_prices_join, on=['item', 'store'])
  4. Out[105]:

4. concat, join, 和merge的区别 - 图12

  1. # 要使用concat,需要将item和store两列放入两个DataFrame的行索引。但是,因为行索引值有重复,造成了错误
  2. In[106]: pd.concat([food_transactions.set_index(['item', 'store']),
  3. food_prices.set_index(['item', 'store'])], axis='columns')
  4. ---------------------------------------------------------------------------
  5. Exception Traceback (most recent call last)
  6. <ipython-input-106-8aa3223bf3d1> in <module>()
  7. 1 pd.concat([food_transactions.set_index(['item', 'store']),
  8. ----> 2 food_prices.set_index(['item', 'store'])], axis='columns')
  9. /Users/Ted/anaconda/lib/python3.6/site-packages/pandas/core/reshape/concat.py in concat(objs, axis, join, join_axes, ignore_index, keys, levels, names, verify_integrity, copy)
  10. 205 verify_integrity=verify_integrity,
  11. 206 copy=copy)
  12. --> 207 return op.get_result()
  13. 208
  14. 209
  15. /Users/Ted/anaconda/lib/python3.6/site-packages/pandas/core/reshape/concat.py in get_result(self)
  16. 399 obj_labels = mgr.axes[ax]
  17. 400 if not new_labels.equals(obj_labels):
  18. --> 401 indexers[ax] = obj_labels.reindex(new_labels)[1]
  19. 402
  20. 403 mgrs_indexers.append((obj._data, indexers))
  21. /Users/Ted/anaconda/lib/python3.6/site-packages/pandas/core/indexes/multi.py in reindex(self, target, method, level, limit, tolerance)
  22. 1861 tolerance=tolerance)
  23. 1862 else:
  24. -> 1863 raise Exception("cannot handle a non-unique multi-index!")
  25. 1864
  26. 1865 if not isinstance(target, MultiIndex):
  27. Exception: cannot handle a non-unique multi-index!
  1. # glob模块的glob函数可以将文件夹中的文件迭代取出,取出的是文件名字符串列表,可以直接传给read_csv函数
  2. In[107]: import glob
  3. df_list = []
  4. for filename in glob.glob('data/gas prices/*.csv'):
  5. df_list.append(pd.read_csv(filename, index_col='Week', parse_dates=['Week']))
  6. gas = pd.concat(df_list, axis='columns')
  7. gas.head()
  8. Out[107]:

4. concat, join, 和merge的区别 - 图13