<SYSTEM_TASK:>
Solve the following problem using Python, implementing the functions described below, one line at a time
<END_TASK>
<USER_TASK:>
Description:
 def perf_attrib(returns, positions, factor_returns, factor_loadings):
 """ Attributes the performance of a returns stream to a set of risk factors. Performance attribution determines how much each risk factor, e.g., momentum, the technology sector, etc., contributed to total returns, as well as the daily exposure to each of the risk factors. The returns that can be attributed to one of the given risk factors are the `common_returns`, and the returns that _cannot_ be attributed to a risk factor are the `specific_returns`. The `common_returns` and `specific_returns` summed together will always equal the total returns. Parameters returns : pd.Series Returns for each day in the date range. - Example: 2017-01-01 -0.017098 2017-01-02 0.002683 2017-01-03 -0.008669 positions: pd.Series Daily holdings in percentages, indexed by date. - Examples: dt ticker 2017-01-01 AAPL 0.417582 TLT 0.010989 XOM 0.571429 2017-01-02 AAPL 0.202381 TLT 0.535714 XOM 0.261905 factor_returns : pd.DataFrame Returns by factor, with date as index and factors as columns - Example: momentum reversal 2017-01-01 0.002779 -0.005453 2017-01-02 0.001096 0.010290 factor_loadings : pd.DataFrame Factor loadings for all days in the date range, with date and ticker as index, and factors as columns. - Example: momentum reversal dt ticker 2017-01-01 AAPL -1.592914 0.852830 TLT 0.184864 0.895534 XOM 0.993160 1.149353 2017-01-02 AAPL -0.140009 -0.524952 TLT -1.066978 0.185435 XOM -1.798401 0.761549 Returns ------- tuple of (risk_exposures_portfolio, perf_attribution) risk_exposures_portfolio : pd.DataFrame df indexed by datetime, with factors as columns - Example: momentum reversal dt 2017-01-01 -0.238655 0.077123 2017-01-02 0.821872 1.520515 perf_attribution : pd.DataFrame df with factors, common returns, and specific returns as columns, and datetimes as index - Example: momentum reversal common_returns specific_returns dt 2017-01-01 0.249087 0.935925 1.185012 1.185012 2017-01-02 -0.003194 -0.400786 -0.403980 -0.403980 Note ---- See https://en.wikipedia.org/wiki/Performance_attribution for more details. """

    risk_exposures_portfolio = compute_exposures(positions,
                                                 factor_loadings)

    perf_attrib_by_factor = risk_exposures_portfolio.multiply(factor_returns)

    common_returns = perf_attrib_by_factor.sum(axis='columns')
    specific_returns = returns - common_returns

    returns_df = pd.DataFrame({'total_returns': returns,
                               'common_returns': common_returns,
                               'specific_returns': specific_returns})

    return (risk_exposures_portfolio,
            pd.concat([perf_attrib_by_factor, returns_df], axis='columns'))