回答が閉じていなかったので、別の方法を回答します。
もし解決されているようであれば、「解決済」にされてはいかがでしょうか。
PHP
1<?php
2// 複数グループがある前提で。
3$groups = array(
4 'グループA' => array(
5 array( 'date' => '0301', '新規' => 23, '退会' => 4 )
6 , array( 'date' => '0302', '新規' => 5, '退会' => 3 )
7 , array( 'date' => '0303', '新規' => 9, '退会' => 3 )
8 )
9 , 'グループB' => array(
10 array( 'date' => '0301', '新規' => 23, '退会' => 4 )
11 , array( 'date' => '0302', '新規' => 5, '退会' => 3 )
12 , array( 'date' => '0303', '新規' => 9, '退会' => 3 )
13 )
14// , 'グループC' => array(
15// array( 'date' => '0304', '新規' => 23, '退会' => 4 )
16// , array( 'date' => '0305', '新規' => 5, '退会' => 3 )
17// , array( 'date' => '0306', '新規' => 9, '退会' => 3 )
18// ) // グループごとにdateがそろわない例
19);
20$res = '';
21foreach ( $groups as $name => $entries ) {
22 $keys = array_keys( current( current( $groups ) ) );
23 $cnt = count( $keys );
24 for ( $i = 0; $i < $cnt; $i++ ) {
25 if ( $i === 0 && $res !== '' ) { continue; }; // グループごとにdateがそろわない場合はこの行をコメントアウト
26 $tr = '';
27 $key = $keys[ $i ];
28 $tag = ( $i === 0 ) ? 'th' : 'td';
29
30 if ( $i === 0 ) {
31 $tr .= _s( '', 'th',' colspan="2"', 2 );
32 } else {
33 if ( $i === 1 ) {
34 $tr .= _s( _h( $name ), 'th',' rowspan="'.( $cnt - 1 ).'"', 2 );
35 };
36 $tr .= _s( _h( $key ), 'th', '', 2 );
37 };
38 foreach ( $entries as $ii => $entry ) {
39 $tr .= _s( _h( $entry[ $key ] ), $tag, '', 2 );
40 }
41 $res .= _s( $tr, 'tr', '', 1 );
42 }
43}
44// 出力する
45echo _s( $res, 'table', '', 0 );
46
47// エスケープ
48function _h( $str ) {
49 $str = (string) $str;
50 return ( 0 === strlen( $str ) ) ? '' : htmlspecialchars( $str, ENT_QUOTES, 'UTF-8' );
51}
52// 描画補助
53function _s( $html, $tag, $attr, $depth = 0 ) {
54 return sprintf( '%5$s%4$s<%2$s%3$s>%1$s%5$s%4$s</%2$s>'
55 , /* %1$s */ $html
56 , /* %2$s */ $tag
57 , /* %3$s */ ( strlen( $attr ) > 0 ) ? ' '.$attr : ''
58 , /* %4$s */ ( $depth > 0 ) ? str_repeat( "\t", $depth ) : ''
59 , /* %5$s */ "\n"
60 );
61}
62//
63 ?>
64